pyfakefs-4.5.4/0000777000000000000000000000000014167600566011501 5ustar 00000000000000pyfakefs-4.5.4/.github/0000777000000000000000000000000014167600566013041 5ustar 00000000000000pyfakefs-4.5.4/.github/ISSUE_TEMPLATE/0000777000000000000000000000000014167600566015224 5ustar 00000000000000pyfakefs-4.5.4/.github/ISSUE_TEMPLATE/bug_report.md0000666000000000000000000000117014147521400017700 0ustar 00000000000000--- name: Bug report about: Create a report to help us improve title: '' labels: '' assignees: '' --- **Describe the bug** A clear and concise description of what the bug is. Please provide a stack trace if available. **How To Reproduce** Please provide a unit test or a minimal code snippet that reproduces the problem. **Your environment** Please run the following and paste the output. ```bash python -c "import platform; print(platform.platform())" python -c "import sys; print('Python', sys.version)" python -c "from pyfakefs.fake_filesystem import __version__; print('pyfakefs', __version__)" ``` pyfakefs-4.5.4/.github/ISSUE_TEMPLATE/feature_request.md0000666000000000000000000000076513757263357020770 0ustar 00000000000000--- name: Feature request about: Suggest an idea for this project title: '' labels: '' assignees: '' --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A description of any alternative solutions or features you've considered. pyfakefs-4.5.4/.github/workflows/0000777000000000000000000000000014167600566015076 5ustar 00000000000000pyfakefs-4.5.4/.github/workflows/builddocs.yml0000666000000000000000000000247514147521400017564 0ustar 00000000000000name: DocBuild on: push: branches: master defaults: run: shell: bash jobs: build_docs: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] python-version: [3.8] steps: - name: Checkout master uses: actions/checkout@v2 with: path: main - name: Get last commit message run: | cd main echo "LAST_COMMIT=$(echo `git log -1 --pretty=%B`)" >> $GITHUB_ENV cd .. - name: Install needed packages run: | pip3 install wheel pip3 install pytest sudo apt update sudo apt-get install python3-sphinx cd main/docs make html - name: Checkout gh-pages uses: actions/checkout@v2 with: ref: gh-pages path: doc - name: Copy and commit changes run: | cp -r main/gh-pages/* doc/master cd doc git config user.name "CI Build" git config user.email "pyfakefs@gmail.com" git add master/* if [ `git status -s | wc -l` = 0 ]; then echo "No changes in built documentation, skipping" exit 0 fi git commit -a -m "$LAST_COMMIT" git push pyfakefs-4.5.4/.github/workflows/dockerfiles/0000777000000000000000000000000014167600566017370 5ustar 00000000000000pyfakefs-4.5.4/.github/workflows/dockerfiles/Dockerfile_centos0000666000000000000000000000251214147521400022720 0ustar 00000000000000# Copyright 2018 John McGehee. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM centos:7 MAINTAINER jmcgeheeiv@users.noreply.github.com ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 ARG github_repo=jmcgeheeiv/pyfakefs ARG github_branch=master RUN yum install -y python3-pip unzip wget RUN useradd -u 1000 pyfakefs RUN mkdir -p work \ && wget https://github.com/$github_repo/archive/$github_branch.zip \ && unzip $github_branch.zip -d work RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs RUN chown -R pyfakefs:pyfakefs work/pyfakefs WORKDIR work/pyfakefs RUN pip3 install -r requirements.txt RUN pip3 install -r extra_requirements.txt USER pyfakefs ENV PYTHONPATH work/pyfakefs ENV TEST_REAL_FS=1 CMD ["python3", "-m", "pyfakefs.tests.all_tests"] pyfakefs-4.5.4/.github/workflows/dockerfiles/Dockerfile_debian0000666000000000000000000000272214147521400022652 0ustar 00000000000000# Copyright 2018 John McGehee. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM debian MAINTAINER jmcgeheeiv@users.noreply.github.com RUN apt-get update && apt-get install -y locales RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 ARG github_repo=jmcgeheeiv/pyfakefs ARG github_branch=master RUN apt-get update && apt-get install -y \ python3-pip \ unzip \ wget RUN apt-get clean RUN useradd -u 1000 pyfakefs RUN mkdir -p work \ && wget https://github.com/$github_repo/archive/$github_branch.zip \ && unzip $github_branch.zip -d work RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs RUN chown -R pyfakefs:pyfakefs work/pyfakefs WORKDIR work/pyfakefs RUN pip3 install -r requirements.txt RUN pip3 install -r extra_requirements.txt USER pyfakefs ENV PYTHONPATH work/pyfakefs ENV TEST_REAL_FS=1 CMD ["python3", "-m", "pyfakefs.tests.all_tests"] pyfakefs-4.5.4/.github/workflows/dockerfiles/Dockerfile_fedora0000666000000000000000000000251114147521400022664 0ustar 00000000000000# Copyright 2018 John McGehee. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM fedora:32 MAINTAINER jmcgeheeiv@users.noreply.github.com ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 ARG github_repo=jmcgeheeiv/pyfakefs ARG github_branch=master RUN dnf install -y python3-pip unzip wget RUN useradd -u 1000 pyfakefs RUN mkdir -p work \ && wget https://github.com/$github_repo/archive/$github_branch.zip \ && unzip $github_branch.zip -d work RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs RUN chown -R pyfakefs:pyfakefs work/pyfakefs WORKDIR work/pyfakefs RUN pip3 install -r requirements.txt RUN pip3 install -r extra_requirements.txt USER pyfakefs ENV PYTHONPATH work/pyfakefs ENV TEST_REAL_FS=1 CMD ["python3", "-m", "pyfakefs.tests.all_tests"] pyfakefs-4.5.4/.github/workflows/dockerfiles/Dockerfile_ubuntu0000666000000000000000000000272214147521400022752 0ustar 00000000000000# Copyright 2018 John McGehee. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. FROM ubuntu MAINTAINER jmcgeheeiv@users.noreply.github.com RUN apt-get update && apt-get install -y locales RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 ARG github_repo=jmcgeheeiv/pyfakefs ARG github_branch=master RUN apt-get update && apt-get install -y \ python3-pip \ unzip \ wget RUN apt-get clean RUN useradd -u 1000 pyfakefs RUN mkdir -p work \ && wget https://github.com/$github_repo/archive/$github_branch.zip \ && unzip $github_branch.zip -d work RUN WORK_DIR=`ls -d work/*`; mv $WORK_DIR work/pyfakefs RUN chown -R pyfakefs:pyfakefs work/pyfakefs WORKDIR work/pyfakefs RUN pip3 install -r requirements.txt RUN pip3 install -r extra_requirements.txt USER pyfakefs ENV PYTHONPATH work/pyfakefs ENV TEST_REAL_FS=1 CMD ["python3", "-m", "pyfakefs.tests.all_tests"] pyfakefs-4.5.4/.github/workflows/dockertests.yml0000666000000000000000000000107214147521400020136 0ustar 00000000000000name: Dockertests on: [push] jobs: dockertests: runs-on: ubuntu-latest strategy: fail-fast: false matrix: docker-image: [centos, debian, fedora, ubuntu] steps: - uses: actions/checkout@v2 - name: Setup docker container run: | docker build -t pyfakefs -f $GITHUB_WORKSPACE/.github/workflows/dockerfiles/Dockerfile_${{ matrix.docker-image }} . --build-arg github_repo=$GITHUB_REPOSITORY --build-arg github_branch=$(basename $GITHUB_REF) - name: Run tests run: docker run -t pyfakefs pyfakefs-4.5.4/.github/workflows/pythonpackage.yml0000666000000000000000000001045314166355054020457 0ustar 00000000000000name: Testsuite on: [push, pull_request] jobs: linter: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] python-version: [3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install linter run: | uname -a python -m pip install flake8 mypy - name: Check syntax and style run: flake8 . --exclude get-pip.py --max-complexity=13 --statistics mypy: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest] python-version: [3.8] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | python -m pip install -r requirements.txt -r extra_requirements.txt python -m pip install mypy==0.812 - name: Run typing checks run: python -m mypy . tests: runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest, windows-2016] python-version: [3.6, 3.7, 3.8, 3.9, "3.10"] include: - python-version: pypy3 os: ubuntu-latest steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Get pip cache dir id: pip-cache run: | python -m pip install --upgrade pip echo "::set-output name=dir::$(pip cache dir)" - name: Cache dependencies id: cache-dep uses: actions/cache@v2 with: path: ${{ steps.pip-cache.outputs.dir }} key: ${{ matrix.os }}-${{ matrix.python-version }}-pip-${{ hashFiles('**/requirements.txt') }}-${{ hashFiles('**/extra_requirements.txt') }} restore-keys: | ${{ matrix.os }}-${{ matrix.python-version }}-pip- - name: Install dependencies run: | pip install wheel pip install -r requirements.txt pip install . - name: Run unit tests without extra packages as non-root user run: | export TEST_REAL_FS=1 python -bb -m pyfakefs.tests.all_tests_without_extra_packages shell: bash - name: Run setup.py test (uses pytest) run: | python setup.py test shell: bash - name: Run unit tests without extra packages as root run: | if [[ '${{ matrix.os }}' != 'windows-2016' ]]; then # provide the same path as non-root to get the correct virtualenv sudo env "PATH=$PATH" python -m pyfakefs.tests.all_tests_without_extra_packages fi shell: bash - name: Install extra dependencies run: | pip install -r extra_requirements.txt shell: bash - name: Run unit tests with extra packages as non-root user run: | python -m pyfakefs.tests.all_tests shell: bash - name: Run pytest tests run: | export PY_VERSION=${{ matrix.python-version }} $GITHUB_WORKSPACE/.github/workflows/run_pytest.sh shell: bash - name: Run performance tests run: | if [[ '${{ matrix.os }}' != 'macOS-latest' ]]; then export TEST_PERFORMANCE=1 python -m pyfakefs.tests.performance_test fi shell: bash dependency-check: runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, windows-2016] python-version: [3.9] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies run: | pip install -r requirements.txt pip install -r extra_requirements.txt pip install pytest-find-dependencies - name: Check dependencies run: python -m pytest --find-dependencies pyfakefs/tests shell: bash pyfakefs-4.5.4/.github/workflows/run_pytest.sh0000666000000000000000000000067214147521400017636 0ustar 00000000000000#!/bin/bash python -m pytest pyfakefs/pytest_tests/pytest_plugin_test.py if [[ $PY_VERSION == '3.6' ]] || [[ $PY_VERSION == '3.7' ]] || [[ $PY_VERSION == '3.8' ]] || [[ $PY_VERSION == '3.9' ]] ; then python -m pytest pyfakefs/pytest_tests/pytest_fixture_test.py fi python -m pytest pyfakefs/pytest_tests/pytest_plugin_failing_helper.py > ./testresult.txt python -m pytest pyfakefs/pytest_tests/pytest_check_failed_plugin_test.py pyfakefs-4.5.4/.gitignore0000666000000000000000000000033214147521400013452 0ustar 00000000000000*.pyc *.egg-info/ .tox/ # Eclipse .settings .project .pydevproject # PyCharm .idea/ # pytest .cache/ .pytest_cache/ # autodoc created by sphinx gh-pages/ # Distribution creation dist/ build/ pyfakefs-4.5.4/CHANGES.md0000666000000000000000000010503514167600026013066 0ustar 00000000000000# pyfakefs Release Notes The released versions correspond to PyPi releases. ## [Version 4.5.4](https://pypi.python.org/pypi/pyfakefs/4.5.4) (2022-01-12) Minor bugfix release. ### Fixes * added missing mocked functions for fake pipe (see [#650](../../issues/650)) * fixed some bytes warnings (see [#651](../../issues/651)) ## [Version 4.5.3](https://pypi.python.org/pypi/pyfakefs/4.5.3) (2021-11-08) Reverts a change in the previous release that could cause a regression. ### Changes * `os.listdir`, `os.scandir` and `pathlib.Path.listdir` now return the directory list in a random order only if explicitly configured in the file system (use `fs.shuffle_listdir_results = True` with `fs` being the file system). In a future version, the default may be changed to better reflect the real filesystem behavior (see [#647](../../issues/647)) ## [Version 4.5.2](https://pypi.python.org/pypi/pyfakefs/4.5.2) (2021-11-07) This is a bugfix release. ### Changes * `os.listdir`, `os.scandir` and `pathlib.Path.listdir` now return the directory list in a random order (see [#638](../../issues/638)) * the `fcntl` module under Unix is now mocked, e.g. all functions have no effect (this may be changed in the future if needed, see [#645](../../issues/645)) ### Fixes * fixed handling of alternative path separator in `os.path.split`, `os.path.splitdrive` and `glob.glob` (see [#632](../../issues/632)) * fixed handling of failed rename due to permission error (see [#643](../../issues/643)) ## [Version 4.5.1](https://pypi.python.org/pypi/pyfakefs/4.5.1) (2021-08-29) This is a bugfix release. ### Fixes * added handling of path-like where missing * improved handling of `str`/`bytes` paths * suppress all warnings while inspecting loaded modules (see [#614](../../issues/614)) * do not import pandas and related modules if it is not patched (see [#627](../../issues/627)) * handle `pathlib.Path.owner()` and `pathlib.Path.group` by returning the current user/group name (see [#629](../../issues/629)) * fixed handling of `use_known_patches=False` (could cause an exception) ### Infrastructure * added test dependency check (see [#608](../../issues/608)) * skip tests failing with ASCII locale (see [#623](../../issues/623)) ## [Version 4.5.0](https://pypi.python.org/pypi/pyfakefs/4.5.0) (2021-06-04) Adds some support for Python 3.10 and basic type checking. ### New Features * added support for some Python 3.10 features: * new method `pathlib.Path.hardlink_to` * new `newline` argument in `pathlib.Path.write_text` * new `follow_symlinks` argument in `pathlib.Path.stat` and `pathlib.Path.chmod` * new 'strict' argument in `os.path.realpath` ### Changes * Python 3.5 has reached its end of life in September 2020 and is no longer supported * `pathlib2` is still supported, but considered to have the same functionality as `pathlib` and is no longer tested separately; the previous behavior broke newer `pathlib` features if `pathlib2` was installed (see [#592](../../issues/592)) ### Fixes * correctly handle byte paths in `os.path.exists` (see [#595](../../issues/595)) * Update `fake_pathlib` to support changes coming in Python 3.10 ([see](https://github.com/python/cpython/pull/19342) * correctly handle UNC paths in `os.path.split` and in directory path evaluation (see [#606](../../issues/606)) ### Infrastructure * added mypy checks in CI (see [#599](../../issues/599)) ## [Version 4.4.0](https://pypi.python.org/pypi/pyfakefs/4.4.0) (2021-02-24) Adds better support for Python 3.8 / 3.9. ### New Features * added support for `pathlib.Path.link_to` (new in Python 3.8) (see [#580](../../issues/580)) * added support for `pathlib.Path.readlink` (new in Python 3.9) (see [#584](../../issues/584)) * added `FakeFilesystem.create_link` convenience method which creates intermittent directories (see [#580](../../issues/580)) ### Fixes * fixed handling of pipe descriptors in the fake filesystem (see [#581](../../issues/581)) * added non-functional argument `effective_ids` to `os.access` (see [#585](../../issues/585)) * correctly handle `os.file` for unreadable files (see [#588](../../issues/588)) ### Infrastructure * added automatic documentation build and check-in ## [Version 4.3.3](https://pypi.python.org/pypi/pyfakefs/4.3.3) (2020-12-20) Another bugfix release. ### Fixes * Reverted one Windows-specific optimization that can break tests under some conditions (see [#573](../../issues/573)) * Setting `os` did not reset `os.sep` and related variables, fixed null device name, added `os.pathsep` and missing `os.path` variables (see [#572](../../issues/572)) ## [Version 4.3.2](https://pypi.python.org/pypi/pyfakefs/4.3.2) (2020-11-26) This is a bugfix release that fixes a regression introduced in version 4.2.0. ### Fixes * `open` calls had not been patched for modules with a name ending with "io" (see [#569](../../issues/569)) ## [Version 4.3.1](https://pypi.python.org/pypi/pyfakefs/4.3.1) (2020-11-23) This is an update to the performance release, with more setup caching and the possibility to disable it. ### Changes * Added caching of patched modules to avoid lookup overhead * Added `use_cache` option and `clear_cache` method to be able to deal with unwanted side effects of the newly introduced caching ### Infrastructure * Moved CI builds to GitHub Actions for performance reasons ## [Version 4.3.0](https://pypi.python.org/pypi/pyfakefs/4.3.0) (2020-11-19) This is mostly a performance release. The performance of the pyfakefs setup has been decreasing sufficiently, especially with the 4.x releases. This release corrects that by making the most expansive feature optional, and by adding some other performance improvements. This shall decrease the setup time by about a factor of 20, and it shall now be comparable to the performance of the 3.4 release. ### Changes * The `patchfs` decorator now expects a positional argument instead of the keyword arguments `fs`. This avoids confusion with the pytest `fs` fixture and conforms to the behavior of `mock.patch`. You may have to adapt the argument order if you use the `patchfs` and `mock.patch` decorators together (see [#566](../../issues/566)) * Default arguments that are file system functions are now _not_ patched by default to avoid a large performance impact. An additional parameter `patch_default_args` has been added that switches this behavior on (see [#567](../../issues/567)). * Added performance improvements in the test setup, including caching the the unpatched modules ## [Version 4.2.1](https://pypi.python.org/pypi/pyfakefs/4.2.1) (2020-11-02) This is a bugfix release that fixes a regression issue. ### Fixes * remove dependency of pyfakefs on `pytest` (regression, see [#565](../../issues/565)) ## [Version 4.2.0](https://pydpi.python.org/pypi/pyfakefs/4.2.0) (2020-11-01) #### New Features * add support for the `buffering` parameter in `open` (see [#549](../../issues/549)) * add possibility to patch `io.open_code` using the new argument `patch_open_code` (since Python 3.8) (see [#554](../../issues/554)) * add possibility to set file system OS via `FakeFilesystem.os` #### Fixes * fix check for link in `os.walk` (see [#559](../../issues/559)) * fix handling of real files in combination with `home` if simulating Posix under Windows (see [#558](../../issues/558)) * do not call fake `open` if called from skipped module (see [#552](../../issues/552)) * do not call fake `pathlib.Path` if called from skipped module (see [#553](../../issues/553)) * fixed handling of `additional_skip_names` with several module components * allow to open existing pipe file descriptor (see [#493](../../issues/493)) * do not truncate file on failed flush (see [#548](../../issues/548)) * suppress deprecation warnings while collecting modules (see [#542](../../issues/542)) * add support for `os.truncate` and `os.ftruncate` (see [#545](../../issues/545)) #### Infrastructure * fixed another problem with CI test scripts not always propagating errors * make sure pytest will work without pyfakefs installed (see [#550](../../issues/550)) ## [Version 4.1.0](https://pypi.python.org/pypi/pyfakefs/4.1.0) (2020-07-12) #### New Features * Added some support for pandas (`read_csv`, `read_excel` and more), and for django file locks to work with the fake filesystem (see [#531](../../issues/531)) #### Fixes * `os.expanduser` now works with a bytes path * Do not override global warnings setting in `Deprecator` (see [#526](../../issues/526)) * Make sure filesystem modules in `pathlib` are patched (see [#527](../../issues/527)) * Make sure that alternative path separators are correctly handled under Windows (see [#530](../../issues/530)) #### Infrastructure * Make sure all temporary files from real fs tests are removed ## [Version 4.0.2](https://pypi.python.org/pypi/pyfakefs/4.0.2) (2020-03-04) This as a patch release that only builds for Python 3. Note that versions 4.0.0 and 4.0.1 will be removed from PyPi to not to be able to install them under Python 2. #### Fixes * Do not build for Python 2 (see [#524](../../issues/524)) ## [Version 4.0.1](https://pypi.python.org/pypi/pyfakefs/4.0.1) (2020-03-03) This as a bug fix release for a regression bug. #### Fixes * Avoid exception if using `flask-restx` (see [#523](../../issues/523)) ## [Version 4.0.0](https://pypi.python.org/pypi/pyfakefs/4.0.0) (2020-03-03) * pyfakefs 4.0.0 drops support for Python 2.7. If you still need Python 2.7, you can continue to use pyfakefs 3.7.x. #### Changes * Removed Python 2.7 and 3.4 support (see [#492](../../issues/492)) #### New Features * Added support for handling keyword-only arguments in some `os` functions * Added possibility to pass additional parameters to `fs` pytest fixture * Added automatic patching of default arguments that are file system functions * Added convenience decorator `patchfs` to patch single functions using the fake filesystem #### Fixes * Added missing `st_ino` in `makedir` (see [#515](../../issues/515)) * Fixed handling of relative paths in `lresolve` / `os.lstat` (see [#516](../../issues/516)) * Fixed handling of byte string paths (see [#517](../../issues/517)) * Fixed `os.walk` if path ends with path separator (see [#512](../../issues/512)) * Fixed handling of empty path in `os.makedirs` (see [#510](../../issues/510)) * Fixed handling of `os.TMPFILE` flag under Linux (see [#509](../../issues/509) and [#511](../../issues/511)) * Adapted fake `pathlib` to changes in Python 3.7.6/3.8.1 (see [#508](../../issues/508)) * Fixed behavior of `os.makedirs` in write-protected directory (see [#507](../../issues/507)) ## [Version 3.7.2](https://pypi.python.org/pypi/pyfakefs/3.7.2) (2020-03-02) This version backports some fixes from master. #### Fixes * Fixed handling of relative paths in `lresolve` / `os.lstat` (see [#516](../../issues/516)) * Fixed `os.walk` if path ends with path separator (see [#512](../../issues/512)) * Fixed handling of empty path in `os.makedirs` (see [#510](../../issues/510)) * Fixed handling of `os.TMPFILE` flag under Linux (see [#509](../../issues/509) and [#511](../../issues/511)) * Fixed behavior of `os.makedirs` in write-protected directory (see [#507](../../issues/507)) ## [Version 3.7.1](https://pypi.python.org/pypi/pyfakefs/3.7.1) (2020-02-14) This version adds support for Python 3.7.6 and 3.8.1. #### Fixes * Adapted fake `pathlib` to changes in Python 3.7.6/3.8.1 (see [#508](../../issues/508)) (backported from master) ## [Version 3.7](https://pypi.python.org/pypi/pyfakefs/3.7) (2019-11-23) This version adds support for Python 3.8. _Note:_ This is the last pyfakefs version that will support Python 2.7 and Python 3.4 (possible bug fix releases notwithstanding). #### New Features * added support for Python 3.8 (see [#504](../../issues/504)) * added preliminary support for Windows-specific `os.stat_result` attributes `tst_file_attributes` and `st_reparse_tag` (see [#504](../../issues/504)) * added support for fake `os.sendfile` (Posix only, Python 3 only) (see [#504](../../issues/504)) #### Fixes * support `devnull` in Windows under Python 3.8 (see [#504](../../issues/504)) * fixed side effect of calling `DirEntry.stat()` under Windows (changed st_nlink) (see [#502](../../issues/502)) * fixed problem of fake modules still referenced after a test in modules loaded during the test (see [#501](../../issues/501) and [#427](../../issues/427)) * correctly handle missing read permission for parent directory (see [#496](../../issues/496)) * raise for `os.scandir` with non-existing directory (see [#498](../../issues/498)) #### Infrastructure * fixed CI tests scripts to always propagate errors (see [#500](../../issues/500)) ## [Version 3.6.1](https://pypi.python.org/pypi/pyfakefs/3.6.1) (2019-10-07) #### Fixes * avoid rare side effect during module iteration in test setup (see [#338](../../issues/338)) * make sure real OS tests are not executed by default (see [#495](../../issues/495)) ## [Version 3.6](https://pypi.python.org/pypi/pyfakefs/3.6) (2019-06-30) #### Changes * removed unneeded parameter `use_dynamic_patch` #### New Features * support for `src_dir_fd` and `dst_dir_fd` arguments in `os.rename`, `os.replace` and `os.link` * added possibility to use modules instead of module names for the `additional_skip_names` argument (see [#482](../../issues/482)) * added argument `allow_root_user` to `Patcher` and `UnitTest` to allow forcing non-root access (see [#474](../../issues/474)) * added basic support for `os.pipe` (see [#473](../../issues/473)) * added support for symlinks in `add_real_directory` * added new public method `add_real_symlink` #### Infrastructure * added check for correctly installed Python 3 version in Travis.CI (see [#487](../../issues/487)) #### Fixes * fixed incorrect argument names for some `os` functions * fake `DirEntry` now implements `os.PathLike` in Python >= 3.6 (see [#483](../../issues/483)) * fixed incorrect argument name for `os.makedirs` (see [#481](../../issues/481)) * avoid pytest warning under Python 2.7 (see [#466](../../issues/466)) * add __next__ to FakeFileWrapper (see [#485](../../issues/485)) ## [Version 3.5.8](https://pypi.python.org/pypi/pyfakefs/3.5.8) (2019-06-21) Another bug-fix release that mainly fixes a regression wih Python 2 that has been introduced in version 3.5.3. #### Fixes * regression: patching build-in `open` under Python 2 broke unit tests (see [#469](../../issues/469)) * fixed writing to file added with `add_real_file` (see [#470](../../issues/470)) * fixed argument name of `FakeIOModule.open` (see [#471](../../pull/471)) #### Infrastructure * more changes to run tests using `python setup.py test` under Python 2 regardless of `pathlib2` presence ## [Version 3.5.7](https://pypi.python.org/pypi/pyfakefs/3.5.7) (2019-02-08) This is mostly a bug-fix release. #### Fixes * regression: `pathlib` did not get patched in the presence of `pathlib2` (see [#467](../../issues/467)) * fixed errors if running the PyCharm debugger under Python 2 (see [#464](../../issues/464)) #### Infrastructure * do not run real file system tests by default (fixes deployment problem, see [#465](../../issues/465)) * make tests run if running `python setup.py test` under Python 2 ## [Version 3.5.6](https://pypi.python.org/pypi/pyfakefs/3.5.6) (2019-01-13) #### Changes * import external `pathlib2` and `scandir` packages first if present (see [#462](../../issues/462)) ## [Version 3.5.5](https://pypi.python.org/pypi/pyfakefs/3.5.5) (2018-12-20) #### Fixes * removed shebang from test files to avoid packaging warnings (see [#461](../../issues/461)) ## [Version 3.5.4](https://pypi.python.org/pypi/pyfakefs/3.5.4) (2018-12-19) #### New Features * added context manager class `Pause` for pause/resume (see [#448](../../issues/448)) #### Fixes * fixed `AttributeError` shown while displaying `fs` in a failing pytest in Python 2 * fixed permission handling for root user * avoid `AttributeError` triggered by modules without `__module__` attribute (see [#460](../../issues/460)) ## [Version 3.5.3](https://pypi.python.org/pypi/pyfakefs/3.5.3) (2018-11-22) This is a minor release to have a version with passing tests for OpenSUSE packaging. #### New Features * automatically patch file system methods imported as another name like `from os.path import exists as my_exists`, including builtin `open` and `io.open` #### Fixes * make tests for access time less strict to account for file systems that do not change it immediately ([#453](../../issues/453)) ## [Version 3.5.2](https://pypi.python.org/pypi/pyfakefs/3.5.2) (2018-11-11) This is mostly a bug-fix release. #### New Features * added support for pause/resume of patching the file system modules ([#448](../../issues/448)) * allow to set current group ID, set current user ID and group ID as `st_uid` and `st_gid` in new files ([#449](../../issues/449)) #### Fixes * fixed using `modules_to_patch` (regression, see [#450](../../issues/450)) * fixed recursion error on unpickling the fake file system ([#445](../../issues/445)) * allow trailing path in `add_real_directory` ([#446](../../issues/446)) ## [Version 3.5](https://pypi.python.org/pypi/pyfakefs/3.5) (2018-10-22) #### Changes * This version of pyfakefs does not support Python 3.3. Python 3.3 users must keep using pyfakefs 3.4.3, or upgrade to a newer Python version. * The deprecation warnings for the old API are now switched on by default. To switch them off for legacy code, use: ```python from pyfakefs.deprecator import Deprecator Deprecator.show_warnings = False ``` #### New Features * Improved automatic patching: * automatically patch methods of a patched file system module imported like `from os.path import exists` ([#443](../../pull/443)) * a module imported as another name (`import os as _os`) is now correctly patched without the need of additional parameters ([#434](../../pull/434)) * automatically patch `Path` if imported like `from pathlib import Path` ([#440](../../issues/440)) * parameter `patch_path` has been removed from `UnitTest` and `Patcher`, the correct patching of `path` imports is now done automatically ([#429](../../pull/429)) * `UnitTest` /`Patcher` arguments can now also be set in `setUpPyfakefs()` ([#430](../../pull/430)) * added possibility to set user ID ([#431](../../issues/431)) * added side_effect option to fake files ([#433](../../pull/433)) * added some support for extended filesystem attributes under Linux ([#423](../../issues/423)) * handle `contents=None` in `create_file()` as empty contents if size not set ([#424](../../issues/424)) * added `pathlib2` support ([#408](../../issues/408)) ([#422](../../issues/422)) * added support for null device ([#418](../../issues/418)) * improved error message for "Bad file descriptor in fake filesystem" ([#419](../../issues/419)) #### Fixes * fixed pytest when both pyfakefs and future are installed ([#441](../../issues/441)) * file timestamps are now updated more according to the real behavior ([#435](../../issues/435)) * fixed a problem related to patching `shutil` functions using `zipfile` ([#427](../../issues/427)) ## [Version 3.4.3](https://pypi.python.org/pypi/pyfakefs/3.4.3) (2018-06-13) This is mostly a bug fix release, mainly for bugs found by [@agroce](https://github.com/agroce) using [tstl](https://github.com/agroce/tstl). #### New Features * added support for path-like objects as arguments in `create_file()`, `create_dir()`, `create_symlink()`, `add_real_file()` and `add_real_directory()` (Python >= 3.6, see [#409](../../issues/409)) #### Infrastructure * moved tests into package * use README.md in pypi ([#358](../../issues/358)) #### Fixes * `tell` after `seek` gave incorrect result in append mode ([#363](../../issues/363)) * a failing pytest did not display the test function correctly ([#381](../../issues/381)) * flushing file contents after truncate was incorrect under some conditions ([#412](../../issues/412)) * `readline()` did not work correctly in binary mode ([#411](../../issues/411)) * `pathlib.Path.resolve()` behaved incorrectly if the path does not exist ([#401](../../issues/401)) * `closed` attribute was not implemented in fake file ([#380](../../issues/380)) * `add_real_directory` did not behave correctly for nested paths * the following functions did not behave correctly for paths ending with a path separator (found by @agroce using [tstl](https://github.com/agroce/tstl)): * `os.rename` ([#400](../../issues/400)) * `os.link` ([#399](../../issues/399), [#407](../../issues/407)) * `os.rmdir` ([#398](../../issues/398)) * `os.mkdir`, `os.makedirs` ([#396](../../issues/396)) * `os.rename` ([#391](../../issues/391), [#395](../../issues/395), [#396](../../issues/396), [#389](../../issues/389), [#406](../../issues/406)) * `os.symlink` ([#371](../../issues/371), [#390](../../issues/390)) * `os.path.isdir` ([#387](../../issues/387)) * `open` ([#362](../../issues/362), [#369](../../issues/369), [#397](../../issues/397)) * `os.path.lexists`, `os.path.islink` ([#365](../../issues/365), [#373](../../issues/373), [#396](../../issues/396)) * `os.remove` ([#360](../../issues/360), [#377](../../issues/377), [#396](../../issues/396)) * `os.stat` ([#376](../../issues/376)) * `os.path.isfile` ([#374](../../issues/374)) * `os.path.getsize` ([#368](../../issues/368)) * `os.lstat` ([#366](../../issues/366)) * `os.path.exists` ([#364](../../issues/364)) * `os.readlink` ([#359](../../issues/359), [#372](../../issues/372), [#392](../../issues/392)) ## [Version 3.4.1](https://pypi.python.org/pypi/pyfakefs/3.4.1) (2018-03-18) This is a bug fix only release. #### Fixes * Missing cleanup after using dynamic patcher let to incorrect behavior of `tempfile` after test execution (regression, see [#356](../../issues/356)) * `add_real_directory` does not work after `chdir` (see [#355](../../issues/355)) ## [Version 3.4](https://pypi.python.org/pypi/pyfakefs/3.4) (2018-03-08) This version of pyfakefs does not support Python 2.6. Python 2.6 users must use pyfakefs 3.3 or earlier. #### New Features * Added possibility to map real files or directories to another path in the fake file system (see [#347](../../issues/347)) * Configuration of `Patcher` and `TestCase`: * Possibility to reload modules is now also available in `Patcher` * Added possibility to add own fake modules via `modules_to_patch` argument (see [#345](../../issues/345)) * Dynamic loading of modules after setup is now on by default and no more considered experimental (see [#340](../../issues/340)) * Added support for file descriptor path parameter in `os.scandir` (Python >= 3.7, Posix only) (see [#346](../../issues/346)) * Added support to fake out backported `scandir` module ([#332](../../issues/332)) * `IOError`/`OSError` exception messages in the fake file system now always start with the message issued in the real file system in Unix systems (see [#202](../../issues/202)) #### Infrastructure * Changed API to be PEP-8 conform ([#186](../../issues/186)). Note: The old API is still available. * Removed Python 2.6 support ([#293](../../issues/293)) * Added usage documentation to GitHub Pages * Added contributing guide * Added flake8 tests to Travis CI #### Fixes * Links in base path in `os.scandir` shall not be resolved ([#350](../../issues/350)) * Fixed unit tests when run on a computer not having umask set to 0022 * Correctly handle newline parameter in `open()` for Python 3, added support for universal newline mode in Python 2 ([#339](../../issues/339)) * Fixed handling of case-changing rename with symlink under MacOS ([#322](../../issues/322)) * Creating a file with a path ending with path separator did not raise ([#320](../../issues/320)) * Fixed more problems related to `flush` ([#302](../../issues/302), [#300](../../issues/300)) * Correctly handle opening files more than once ([#343](../../issues/343)) * Fake `os.lstat()` crashed with several trailing path separators ([#342](../../issues/342)) * Fixed handling of path components starting with a drive letter([#337](../../issues/337)) * Symlinks to absolute paths were incorrectly resolved under Windows ([#341](../../issues/341)) * Unittest mock didn't work after setUpPyfakefs ([#334](../../issues/334)) * `os.path.split()` and `os.path.dirname()` gave incorrect results under Windows ([#335](../../issues/335)) ## [Version 3.3](https://pypi.python.org/pypi/pyfakefs/3.3) (2017-11-12) This is the last release that supports Python 2.6. #### New Features * The OS specific temp directory is now automatically created in `setUp()` (related to [#191](../../issues/191)). Note that this may break test code that assumes that the fake file system is completely empty at test start. * Added possibility to reload modules and switch on dynamic loading of modules after setup (experimental, see [#248](../../issues/248)) * Added possibility to patch modules that import file system modules under another name, for example `import os as '_os` ([#231](../../issues/231)) * Added support for `dir_fd` argument in several `os` functions ([#206](../../issues/206)) * Added support for open file descriptor as path argument in `os.utime`, `os.chmod`, `os.chdir`, `os.chown`, `os.listdir`, `os.stat` and `os.lstat` (Python >= 3.3) ([#205](../../issues/205)) * Added support for basic modes in fake `os.open()` ([#204](../../issues/204)) * Added fake `os.path.samefile` implementation ([#193](../../issues/193)) * Added support for `ns` argument in `os.utime()` (Python >= 3.3) ([#192](../../issues/192)) * Added nanosecond time members in `os.stat_result` (Python >= 3.3) ([#196](../../issues/196)) #### Infrastructure * Added Travis CI tests for MacOSX (Python 2.7 and 3.6) * Added Appveyor CI tests for Windows (Python 2.7, 3.3 and 3.6) * Added auto-generated documentation for development version on GitHub Pages * Removed most of `fake_filesystem_shutil` implementation, relying on the patched `os` module instead ([#194](../../issues/194)) * Removed `fake_tempfile` and `fake_filesystem_glob`, relying on the patched `os` module instead ([#189](../../issues/189), [#191](../../issues/191)) #### Fixes * Multiple fixes of bugs found using TSTL by @agroce (see about 100 issues with the `TSTL` label) * several problems with buffer handling in high-level IO functions * several problems with multiple handles on the same file * several problems with low-level IO functions * incorrect exception (`IOError` vs `OSError`) raised in several cases * Fake `rename` did not behave like `os.rename` in many cases * Symlinks have not been considered or incorrectly handled in several functions * A nonexistent file that has the same name as the content of the parent object was seen as existing * Incorrect error handling during directory creation * many fixes for OS-specific behavior * Also patch modules that are loaded between `__init__()` and `setUp()` ([#199](../../issues/199)) * Creating files in read-only directory was possible ([#203](../../issues/203)) ## [Version 3.2](https://pypi.python.org/pypi/pyfakefs/3.2) (2017-05-27) #### New Features * The `errors` argument is supported for `io.open()` and `os.open()` * New methods `add_real_file()`, `add_real_directory()` and `add_real_paths()` make real files and directories appear within the fake file system. File contents are read from the real file system only as needed ([#170](../../issues/170)). See `example_test.py` for a usage example. * Deprecated `TestCase.copyRealFile()` in favor of `add_real_file()`. `copyRealFile()` remains only for backward compatability. Also, some less-popular argument combinations have been disallowed. * Added this file you are reading, `CHANGES.md`, to the release manifest #### Infrastructure * The `mox3` package is no longer a prerequisite--the portion required by pyfakefs has been integrated into pyfakefs ([#182](../../issues/182)) #### Fixes * Corrected the handling of byte/unicode paths in several functions ([#187](../../issues/187)) * `FakeShutilModule.rmtree()` failed for directories ending with path separator ([#177](../../issues/177)) * Case was incorrectly handled for added Windows drives * `pathlib.glob()` incorrectly handled case under MacOS ([#167](../../issues/167)) * tox support was broken ([#163](../../issues/163)) * On Windows it was not possible to rename a file when only the case of the file name changed ([#160](../../issues/160)) ## [Version 3.1](https://pypi.python.org/pypi/pyfakefs/3.1) (2017-02-11) #### New Features * Added helper method `TestCase.copyRealFile()` to copy a file from the real file system to the fake file system. This makes it easy to use template, data and configuration files in your tests. * A pytest plugin is now installed with pyfakefs that exports the fake filesystem as pytest fixture `fs`. #### Fixes * Incorrect disk usage calculation if too large file created ([#155](../../issues/155)) ## [Version 3.0](https://pypi.python.org/pypi/pyfakefs/3.0) (2017-01-18) #### New Features * Support for path-like objects as arguments in fake `os` and `os.path` modules (Python >= 3.6) * Some changes to make pyfakefs work with Python 3.6 * Added fake `pathlib` module (Python >= 3.4) ([#29](../../issues/29)) * Support for `os.replace` (Python >= 3.3) * `os.access`, `os.chmod`, `os.chown`, `os.stat`, `os.utime`: support for `follow_symlinks` argument (Python >= 3.3) * Support for `os.scandir` (Python >= 3.5) ([#119](../../issues/119)) * Option to not fake modules named `path` ([#53](../../issues/53)) * `glob.glob`, `glob.iglob`: support for `recursive` argument (Python >= 3.5) ([#116](../../issues/116)) * Support for `glob.iglob` ([#59](../../issues/59)) #### Infrastructure * Added [auto-generated documentation](http://jmcgeheeiv.github.io/pyfakefs/) #### Fixes * `shutil.move` incorrectly moves directories ([#145](../../issues/145)) * Missing support for 'x' mode in `open` (Python >= 3.3) ([#147](../../issues/147)) * Incorrect exception type in Posix if path ancestor is a file ([#139](../../issues/139)) * Exception handling when using `Patcher` with py.test ([#135](../../issues/135)) * Fake `os.listdir` returned sorted instead of unsorted entries ## [Version 2.9](https://pypi.python.org/pypi/pyfakefs/2.9) (2016-10-02) #### New Features * `io.open`, `os.open`: support for `encoding` argument ([#120](../../issues/120)) * `os.makedirs`: support for `exist_ok` argument (Python >= 3.2) ([#98](../../issues/98)) * Support for fake `io.open()` ([#70](../../issues/70)) * Support for mount points ([#25](../../issues/25)) * Support for hard links ([#75](../../issues/75)) * Support for float times (mtime, ctime) * Windows support: * support for alternative path separator * support for case-insensitive filesystems ([#69](../../issues/69)) * support for drive letters and UNC paths * Support for filesystem size ([#86](../../issues/86)) * `shutil.rmtree`: support for `ignore_errors` and `onerror` arguments ([#72](../../issues/72)) * Support for `os.fsync()` and `os.fdatasync()` ([#73](../../issues/73)) * `os.walk`: Support for `followlinks` argument #### Fixes * `shutil` functions like `make_archive` do not work with pyfakefs ([#104](../../issues/104)) * File permissions on deletion not correctly handled ([#27](../../issues/27)) * `shutil.copy` error with bytes contents ([#105](../../issues/105)) * mtime and ctime not updated on content changes ## [Version 2.7](https://pypi.python.org/pypi/pyfakefs/2.7) #### Infrastructure * Moved repository from GoogleCode to GitHub, merging 3 projects * Added continuous integration testing with Travis CI * Added usage documentation in project wiki * Better support for pypi releases #### New Features * Added direct unit test support in `fake_filesystem_unittest` (transparently patches all calls to faked implementations) * Added support for doctests * Added support for cygwin * Better support for Python 3 #### Fixes * `os.utime` fails to traverse symlinks ([#49](../../issues/49)) * `chown` incorrectly accepts non-integer uid/gid arguments ([#30](../../issues/30)) * Reading from fake block devices doesn't work ([#24](../../issues/24)) * `fake_tempfile` is using `AddOpenFile` incorrectly ([#23](../../issues/23)) * Incorrect behavior of `relpath`, `abspath` and `normpath` on Windows. * Cygwin wasn't treated as Windows ([#37](../../issues/37)) * Python 3 `open` in binary mode not working ([#32](../../issues/32)) * `os.remove` doesn't work with relative paths ([#31](../../issues/31)) * `mkstemp` returns no valid file descriptor ([#19](../../issues/19)) * `open` methods lack `IOError` for prohibited operations ([#18](../../issues/18)) * Incorrectly resolved relative path ([#3](../../issues/3)) * `FakeFileOpen` keyword args do not match the `__builtin__` equivalents ([#5](../../issues/5)) * Relative paths not supported ([#16](../../issues/16), [#17](../../issues/17))) ## Older Versions There are no release notes for releases 2.6 and below. The following versions are still available on PyPi: * [1.1](https://pypi.python.org/pypi/pyfakefs/1.1), [1.2](https://pypi.python.org/pypi/pyfakefs/1.2), [2.0](https://pypi.python.org/pypi/pyfakefs/2.0), [2.1](https://pypi.python.org/pypi/pyfakefs/2.1), [2.2](https://pypi.python.org/pypi/pyfakefs/2.2), [2.3](https://pypi.python.org/pypi/pyfakefs/2.3) and [2.4](https://pypi.python.org/pypi/pyfakefs/2.4) pyfakefs-4.5.4/CONTRIBUTING.md0000666000000000000000000000660313757263357013746 0ustar 00000000000000 # Contributing to pyfakefs We welcome any contributions that help to improve pyfakefs for the community. Contributions may include bug reports, bug fixes, new features, infrastructure enhancements, or documentation updates. ## How to contribute ### Reporting Bugs If you think you found a bug in pyfakefs, you can [create an issue](https://help.github.com/articles/creating-an-issue/). Before filing the bug, please check, if it still exists in the [master branch](https://github.com/jmcgeheeiv/pyfakefs). If you can reproduce the problem, please provide enough information so that it can be reproduced by other developers. This includes: * The Operating System * The Python version * A minimal example to reproduce the problem (preferably in the form of a failing test) * The stack trace in case of an unexpected exception. For better readability, you may use [markdown code formatting](https://help.github.com/articles/creating-and-highlighting-code-blocks/) for any included code. ### Proposing Enhancements If you need a specific feature that is not implemented, or have an idea for the next exciting gimmick in pyfakefs, you can also create a respective issue. Of course - implementing it yourself is the best chance to get it done! The next item has some information on doing this. ### Contributing Code The preferred workflow for contributing code is to [fork](https://help.github.com/articles/fork-a-repo/) the [repository](https://github.com/jmcgeheeiv/pyfakefs) on GitHub, clone it, develop on a feature branch, and [create a pull request](https://help.github.com/articles/creating-a-pull-request-from-a-fork) when done. There are a few things to consider for contributing code: * Please use the standard [PEP-8 coding style](https://www.python.org/dev/peps/pep-0008/) (your IDE or tools like [pep8](https://pypi.python.org/pypi/pep8) or [pylint](https://pypi.python.org/pypi/pylint) will help you) * Use the [Google documentation style](https://google.github.io/styleguide/pyguide.html) to document new public classes or methods * Provide unit tests for bug fixes or new functionality - check the existing tests for examples * Provide meaningful commit messages - it is ok to amend the commits to improve the comments * Check that the automatic tests on [Travis](https://travis-ci.org/jmcgeheeiv/pyfakefs) and [AppVeyor](https://ci.appveyor.com/project/jmcgeheeiv/pyfakefs) all pass for your pull request * Be ready to adapt your changes after a code review ### Contributing Documentation If you want to improve the existing documentation, you can do this also using a pull request. You can contribute to: * the source code documentation using [Google documentation style](https://google.github.io/styleguide/pyguide.html) * the [README](https://github.com/jmcgeheeiv/pyfakefs/blob/master/README.md) using [markdown syntax](https://help.github.com/articles/basic-writing-and-formatting-syntax/) * the documentation published on [GitHub Pages](http://jmcgeheeiv.github.io/pyfakefs/), located in the `docs` directory (call `make html` from that directory). For building the documentation, you will need [sphinx](http://sphinx.pocoo.org/). * [this file](https://github.com/jmcgeheeiv/pyfakefs/blob/master/CONTRIBUTING.md) if you want to enhance the contributing guide itself Thanks for taking the time to contribute to pyfakefs! pyfakefs-4.5.4/COPYING0000666000000000000000000002411513235403700012521 0ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. pyfakefs-4.5.4/Dockerfile0000666000000000000000000000313213757263357013501 0ustar 00000000000000# Copyright 2018 John McGehee. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Prerequisites: # * Install Docker # * Clone pyfakefs # # To build and run the container: # # cd pyfakefs # docker build -t pyfakefs . # docker run -t pyfakefs FROM ubuntu MAINTAINER jmcgeheeiv@users.noreply.github.com # The Ubuntu base container does not specify a locale. # pyfakefs tests require at least the Latin1 character set. RUN apt-get update && apt-get install -y locales RUN locale-gen en_US.UTF-8 ENV LANG en_US.UTF-8 ENV LANGUAGE en_US:en ENV LC_ALL en_US.UTF-8 RUN apt-get update && apt-get install -y \ python3-pip \ unzip \ wget RUN apt-get clean RUN useradd -u 1000 pyfakefs RUN wget https://github.com/jmcgeheeiv/pyfakefs/archive/master.zip \ && unzip master.zip \ && chown -R pyfakefs:pyfakefs /pyfakefs-master WORKDIR /pyfakefs-master RUN pip3 install -r requirements.txt RUN pip3 install -r extra_requirements.txt USER pyfakefs ENV PYTHONPATH /pyfakefs-master CMD ["python3", "-m", "pyfakefs.tests.all_tests"] pyfakefs-4.5.4/docs/0000777000000000000000000000000014167600566012431 5ustar 00000000000000pyfakefs-4.5.4/docs/api.rst0000666000000000000000000000157213757263357013750 0ustar 00000000000000API Notes ========= With ``pyfakefs 3.4``, the public API has changed to be PEP-8 conform. The old API is deprecated, and will be removed in some future version of pyfakefs. You can suppress the deprecation warnings for legacy code with the following code: .. code:: python from pyfakefs.deprecator import Deprecator Deprecator.show_warnings = False Here is a list of selected changes: :pyfakefs.fake_filesystem.FakeFileSystem: CreateFile() -> create_file() CreateDirectory() -> create_dir() CreateLink() -> create_symlink() GetDiskUsage() -> get_disk_usage() SetDiskUsage() -> set_disk_usage() :pyfakefs.fake_filesystem.FakeFile: GetSize(), SetSize() -> size (property) SetContents() -> set_contents() SetATime() -> st_atime (property) SetMTime() -> st_mtime (property) SetCTime() -> st_ctime (property) pyfakefs-4.5.4/docs/autopatch.rst0000666000000000000000000001026713757263357015170 0ustar 00000000000000.. _auto_patch: Automatically find and patch file functions and modules ======================================================= The ``fake_filesystem_unittest`` module automatically finds all real file functions and modules, and stubs them out with the fake file system functions and modules. The pyfakefs source code contains files that demonstrate this usage model: - ``example.py`` is the software under test. In production, it uses the real file system. - ``example_test.py`` tests ``example.py``. During testing, the pyfakefs fake file system is used by ``example_test.py`` and ``example.py`` alike. Software Under Test ------------------- ``example.py`` contains a few functions that manipulate files. For instance: .. code:: python def create_file(path): '''Create the specified file and add some content to it. Use the open() built in function. For example, the following file operations occur in the fake file system. In the real file system, we would not even have permission to write /test: >>> os.path.isdir('/test') False >>> os.mkdir('/test') >>> os.path.isdir('/test') True >>> os.path.exists('/test/file.txt') False >>> create_file('/test/file.txt') >>> os.path.exists('/test/file.txt') True >>> with open('/test/file.txt') as f: ... f.readlines() ["This is test file '/test/file.txt'.\\n", 'It was created using the open() function.\\n'] ''' with open(path, 'w') as f: f.write("This is test file '{}'.\n".format(path)) f.write("It was created using the open() function.\n") No functional code in ``example.py`` even hints at a fake file system. In production, ``create_file()`` invokes the real file functions ``open()`` and ``write()``. Unit Tests and Doctests ----------------------- ``example_test.py`` contains unit tests for ``example.py``. ``example.py`` contains the doctests, as you can see above. The module ``fake_filesystem_unittest`` contains code that finds all real file functions and modules, and stubs these out with the fake file system functions and modules: .. code:: python import os import unittest from pyfakefs import fake_filesystem_unittest # The module under test is example: import example Doctests ~~~~~~~~ ``example_test.py`` defines ``load_tests()``, which runs the doctests in ``example.py``: .. code:: python def load_tests(loader, tests, ignore): '''Load the pyfakefs/example.py doctest tests into unittest.''' return fake_filesystem_unittest.load_doctests(loader, tests, ignore, example) Everything, including all imported modules and the test, is stubbed out with the fake filesystem. Thus you can use familiar file functions like ``os.mkdir()`` as part of your test fixture and they too will operate on the fake file system. Unit Test Class ~~~~~~~~~~~~~~~ Next comes the ``unittest`` test class. This class is derived from ``fake_filesystem_unittest.TestCase``, which is in turn derived from ``unittest.TestClass``: .. code:: python class TestExample(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs() def tearDown(self): # It is no longer necessary to add self.tearDownPyfakefs() pass def test_create_file(self): '''Test example.create_file()''' # The os module has been replaced with the fake os module so all of the # following occurs in the fake filesystem. self.assertFalse(os.path.isdir('/test')) os.mkdir('/test') self.assertTrue(os.path.isdir('/test')) self.assertFalse(os.path.exists('/test/file.txt')) example.create_file('/test/file.txt') self.assertTrue(os.path.exists('/test/file.txt')) ... Just add ``self.setUpPyfakefs()`` in ``setUp()``. You need add nothing to ``tearDown()``. Write your tests as usual. From ``self.setUpPyfakefs()`` to the end of your ``tearDown()`` method, all file operations will use the fake file system. pyfakefs-4.5.4/docs/conf.py0000666000000000000000000002442614167577652013751 0ustar 00000000000000#!/usr/bin/env python3 # -*- coding: utf-8 -*- # # pyfakefs documentation build configuration file, created by # sphinx-quickstart on Mon Oct 31 20:05:53 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # import os import sys sys.path.insert( 0, os.path.split(os.path.dirname(os.path.abspath(__file__)))[0]) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.githubpages', # puts .nojekyll file into source 'sphinx.ext.napoleon' # enables google style docstrings ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] source_suffix = '.rst' # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'pyfakefs' copyright = '''2009 Google Inc. All Rights Reserved. © Copyright 2014 Altera Corporation. All Rights Reserved. © Copyright 2014-2022 John McGehee''' author = 'John McGehee' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '4.5.4' # The full version, including alpha/beta/rc tags. release = '4.5.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False autoclass_content = 'both' autodoc_member_order = 'bysource' # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'pyfakefs_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. html_theme_path = ['.'] # The name for this set of Sphinx documents. # " v documentation" by default. # # html_title = 'pyfakefs v3.4' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = [] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # html_show_sphinx = False # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'pyfakefsdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'pyfakefs.tex', 'pyfakefs Documentation', 'John McGehee', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'pyfakefs', 'pyfakefs Documentation', [author], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'pyfakefs', 'pyfakefs Documentation', author, 'pyfakefs', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False pyfakefs-4.5.4/docs/index.rst0000666000000000000000000000075013756767653014312 0ustar 00000000000000.. pyfakefs documentation master file, created by sphinx-quickstart on Mon Oct 31 20:05:53 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Welcome to the pyfakefs documentation! ====================================== Contents: .. toctree:: :maxdepth: 2 intro usage autopatch modules api Indices and tables ================== * :ref:`genindex` * :ref:`search` pyfakefs-4.5.4/docs/intro.rst0000666000000000000000000001127714147521400014311 0ustar 00000000000000Introduction ============ `pyfakefs `__ implements a fake file system that mocks the Python file system modules. Using pyfakefs, your tests operate on a fake file system in memory without touching the real disk. The software under test requires no modification to work with pyfakefs. pyfakefs works with CPython 3.6 and above, on Linux, Windows and OSX (MacOS), and with PyPy3. pyfakefs works with `PyTest `__ version 2.8.6 or above. Installation ------------ pyfakefs is available on `PyPi `__. The latest released version can be installed from pypi: .. code:: bash pip install pyfakefs The latest master can be installed from the GitHub sources: .. code:: bash pip install git+https://github.com/jmcgeheeiv/pyfakefs Features -------- - Code executed under pyfakefs works transparently on a memory-based file system without the need of special commands. The same code that works on the real filesystem will work on the fake filesystem if running under pyfakefs. - pyfakefs provides direct support for `unittest` (via a `TestCase` base class) and `pytest` (via a fixture), but can also be used with other test frameworks. - Each pyfakefs test starts with an empty file system, but it is possible to map files and directories from the real file system into the fake filesystem if needed. - No files in the real file system are changed during the tests, even in the case of writing to mapped real files. - pyfakefs keeps track of the filesystem size if configured. The file system size can be configured arbitrarily. - it is possible to pause and resume using the fake filesystem, if the real file system has to be used in a test step - pyfakefs defaults to the OS it is running on, but can also be configured to test code running under another OS (Linux, MacOS or Windows). - pyfakefs can be configured to behave as if running as a root or as a non-root user, independently from the actual user. .. _limitations: Limitations ----------- - pyfakefs will not work with Python libraries (other than `os` and `io`) that use C libraries to access the file system, because it cannot patch the underlying C libraries' file access functions - pyfakefs patches most kinds of importing file system modules automatically, but there are still some cases where this will not work. See :ref:`customizing_patcher` for more information and ways to work around this. - pyfakefs does not retain the MRO for file objects, so you cannot rely on checks using `isinstance` for these objects (for example, to differentiate between binary and textual file objects). - pyfakefs is only tested with CPython and the newest PyPy versions, other Python implementations will probably not work - Differences in the behavior in different Linux distributions or different MacOS or Windows versions may not be reflected in the implementation, as well as some OS-specific low-level file system behavior. The systems used for automatic tests in `Travis.CI `__ and `AppVeyor `__ are considered as reference systems, additionally the tests are run in Docker containers with the latest CentOS, Debian, Fedora and Ubuntu images. - pyfakefs may not work correctly if file system functions are patched by other means (e.g. using `unittest.mock.patch`) - see :ref:`usage_with_mock_open` for more information History ------- pyfakefs was initially developed at Google by `Mike Bland `__ as a modest fake implementation of core Python modules. It was introduced to all of Google in September 2006. Since then, it has been enhanced to extend its functionality and usefulness. At last count, pyfakefs was used in over 2,000 Python tests at Google. Google released pyfakefs to the public in 2011 as Google Code project `pyfakefs `__: * Fork `jmcgeheeiv-pyfakefs `__ added direct support for unittest and doctest as described in :ref:`auto_patch` * Fork `shiffdane-jmcgeheeiv-pyfakefs `__ added further corrections After the `shutdown of Google Code `__ was announced, `John McGehee `__ merged all three Google Code projects together `on GitHub `__ where an enthusiastic community actively maintains and extends pyfakefs. pyfakefs-4.5.4/docs/make.bat0000777000000000000000000001706313756767653014066 0ustar 00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=../gh-pages set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. epub3 to make an epub3 echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled echo. dummy to check syntax errors of document sources goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 1>NUL 2>NUL if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR% if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\pyfakefs.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\pyfakefs.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "epub3" ( %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) if "%1" == "dummy" ( %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy if errorlevel 1 exit /b 1 echo. echo.Build finished. Dummy builder generates no files. goto end ) :end pyfakefs-4.5.4/docs/Makefile0000666000000000000000000001723213756767653014114 0ustar 00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = ../gh-pages # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " epub3 to make an epub3" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" @echo " dummy to check syntax errors of document sources" .PHONY: clean clean: rm -rf $(BUILDDIR)/* .PHONY: html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR) @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)." .PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/pyfakefs.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/pyfakefs.qhc" .PHONY: applehelp applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/pyfakefs" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/pyfakefs" @echo "# devhelp" .PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: epub3 epub3: $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 @echo @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." .PHONY: latex latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." .PHONY: dummy dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo @echo "Build finished. Dummy builder generates no files." pyfakefs-4.5.4/docs/modules.rst0000666000000000000000000000352013757263357014642 0ustar 00000000000000Public Modules and Classes ========================== .. note:: Only public classes and methods interesting to ``pyfakefs`` users are shown. Methods that mimic the behavior of standard Python functions and classes that are only needed internally are not listed. Fake filesystem module ---------------------- .. automodule:: pyfakefs.fake_filesystem :members: set_uid, set_gid Fake filesystem classes ----------------------- .. autoclass:: pyfakefs.fake_filesystem.FakeFilesystem :members: add_mount_point, get_disk_usage, set_disk_usage, add_real_directory, add_real_file, add_real_symlink, add_real_paths, create_dir, create_file, create_symlink, get_object, pause, resume .. autoclass:: pyfakefs.fake_filesystem.FakeFile :members: byte_contents, contents, set_contents, path, size, is_large_file .. autoclass:: pyfakefs.fake_filesystem.FakeDirectory :members: contents, ordered_dirs, size, get_entry, remove_entry Unittest module classes ----------------------- .. autoclass:: pyfakefs.fake_filesystem_unittest.TestCaseMixin :members: fs, setUpPyfakefs, pause, resume .. autoclass:: pyfakefs.fake_filesystem_unittest.TestCase .. autoclass:: pyfakefs.fake_filesystem_unittest.Patcher :members: setUp, tearDown, pause, resume .. automodule:: pyfakefs.fake_filesystem_unittest :members: patchfs Faked module classes -------------------- .. autoclass:: pyfakefs.fake_filesystem.FakeOsModule .. autoclass:: pyfakefs.fake_filesystem.FakePathModule .. autoclass:: pyfakefs.fake_filesystem.FakeFileOpen .. autoclass:: pyfakefs.fake_filesystem.FakeIoModule .. autoclass:: pyfakefs.fake_filesystem_shutil.FakeShutilModule .. autoclass:: pyfakefs.fake_pathlib.FakePathlibModule .. autoclass:: pyfakefs.fake_scandir.FakeScanDirModule pyfakefs-4.5.4/docs/pyfakefs_theme/0000777000000000000000000000000014167600566015423 5ustar 00000000000000pyfakefs-4.5.4/docs/pyfakefs_theme/static/0000777000000000000000000000000014167600566016712 5ustar 00000000000000pyfakefs-4.5.4/docs/pyfakefs_theme/static/pyfakefs.css0000666000000000000000000000021313756767653021246 0ustar 00000000000000@import url("nature.css"); .code div pre { background-color: #F5F5F5; } .highlight pre { background-color: #F5F5F5; } pyfakefs-4.5.4/docs/pyfakefs_theme/theme.conf0000666000000000000000000000006613756767653017414 0ustar 00000000000000[theme] inherit = nature stylesheet = pyfakefs.css pyfakefs-4.5.4/docs/usage.rst0000666000000000000000000010450114147521400014253 0ustar 00000000000000Usage ===== Test Scenarios -------------- There are several approaches for implementing tests using ``pyfakefs``. Patch using fake_filesystem_unittest ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are using the Python ``unittest`` package, the easiest approach is to use test classes derived from ``fake_filesystem_unittest.TestCase``. If you call ``setUpPyfakefs()`` in your ``setUp()``, ``pyfakefs`` will automatically find all real file functions and modules, and stub these out with the fake file system functions and modules: .. code:: python from pyfakefs.fake_filesystem_unittest import TestCase class ExampleTestCase(TestCase): def setUp(self): self.setUpPyfakefs() def test_create_file(self): file_path = '/test/file.txt' self.assertFalse(os.path.exists(file_path)) self.fs.create_file(file_path) self.assertTrue(os.path.exists(file_path)) The usage is explained in more detail in :ref:`auto_patch` and demonstrated in the files `example.py`_ and `example_test.py`_. Patch using the pytest plugin ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you use `pytest`_, you will be interested in the pytest plugin in ``pyfakefs``. This automatically patches all file system functions and modules in a similar manner as described above. The pytest plugin provides the ``fs`` fixture for use in your test. The plugin is registered for pytest on installing ``pyfakefs`` as usual for pytest plugins, so you can just use it: .. code:: python def my_fakefs_test(fs): # "fs" is the reference to the fake file system fs.create_file('/var/data/xx1.txt') assert os.path.exists('/var/data/xx1.txt') If you are bothered by the ``pylint`` warning, ``C0103: Argument name "fs" doesn't conform to snake_case naming style (invalid-name)``, you can define a longer name in your ``conftest.py`` and use that in your tests: .. code:: python @pytest.fixture def fake_filesystem(fs): # pylint:disable=invalid-name """Variable name 'fs' causes a pylint warning. Provide a longer name acceptable to pylint for use in tests. """ yield fs Patch using fake_filesystem_unittest.Patcher ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you are using other means of testing like `nose`_, you can do the patching using ``fake_filesystem_unittest.Patcher``--the class doing the actual work of replacing the filesystem modules with the fake modules in the first two approaches. The easiest way is to just use ``Patcher`` as a context manager: .. code:: python from pyfakefs.fake_filesystem_unittest import Patcher with Patcher() as patcher: # access the fake_filesystem object via patcher.fs patcher.fs.create_file('/foo/bar', contents='test') # the following code works on the fake filesystem with open('/foo/bar') as f: contents = f.read() You can also initialize ``Patcher`` manually: .. code:: python from pyfakefs.fake_filesystem_unittest import Patcher patcher = Patcher() patcher.setUp() # called in the initialization code ... patcher.tearDown() # somewhere in the cleanup code Patch using fake_filesystem_unittest.patchfs decorator ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ This is basically a convenience wrapper for the previous method. If you are not using ``pytest`` and want to use the fake filesystem for a single function, you can write: .. code:: python from pyfakefs.fake_filesystem_unittest import patchfs @patchfs def test_something(fake_fs): # access the fake_filesystem object via fake_fs fake_fs.create_file('/foo/bar', contents='test') Note that ``fake_fs`` is a positional argument and the argument name does not matter. If there are additional ``mock.patch`` decorators that also create positional arguments, the argument order is the same as the decorator order, as shown here: .. code:: python @patchfs @mock.patch('foo.bar') def test_something(fake_fs, mocked_bar): ... @mock.patch('foo.bar') @patchfs def test_something(mocked_bar, fake_fs): ... .. note:: Avoid writing the ``patchfs`` decorator *between* ``mock.patch`` operators, as the order will not be what you expect. Due to implementation details, all arguments created by ``mock.patch`` decorators are always expected to be contiguous, regardless of other decorators positioned between them. .. caution:: In previous versions, the keyword argument `fs` has been used instead, which had to be positioned *after* all positional arguments regardless of the decorator order. If you upgrade from a version before pyfakefs 4.2, you may have to adapt the argument order. You can also use this to make a single unit test use the fake fs: .. code:: python class TestSomething(unittest.TestCase): @patchfs def test_something(self, fs): fs.create_file('/foo/bar', contents='test') .. _customizing_patcher: Customizing patching -------------------- ``fake_filesystem_unittest.Patcher`` provides a few arguments to adapt patching for cases where it does not work out of the box. These arguments can also be used with ``unittest`` and ``pytest``. Using custom arguments ~~~~~~~~~~~~~~~~~~~~~~ The following sections describe how to apply these arguments in different scenarios, using the argument :ref:`allow_root_user` as an example. Patcher ....... If you use the ``Patcher`` directly, you can just pass the arguments in the constructor: .. code:: python from pyfakefs.fake_filesystem_unittest import Patcher with Patcher(allow_root_user=False) as patcher: ... Unittest ........ If you are using ``fake_filesystem_unittest.TestCase``, the arguments can be passed to ``setUpPyfakefs()``, which will pass them to the ``Patcher`` instance: .. code:: python from pyfakefs.fake_filesystem_unittest import TestCase class SomeTest(TestCase): def setUp(self): self.setUpPyfakefs(allow_root_user=False) def testSomething(self): ... Pytest ...... In case of ``pytest``, you have two possibilities: - The standard way to customize the ``fs`` fixture is to write your own fixture which uses the ``Patcher`` with arguments as has been shown above: .. code:: python import pytest from pyfakefs.fake_filesystem_unittest import Patcher @pytest.fixture def fs_no_root(): with Patcher(allow_root_user=False) as patcher: yield patcher.fs def test_something(fs_no_root): ... - You can also pass the arguments using ``@pytest.mark.parametrize``. Note that you have to provide `all Patcher arguments`_ before the needed ones, as keyword arguments cannot be used, and you have to add ``indirect=True``. This makes it less readable, but gives you a quick possibility to adapt a single test: .. code:: python import pytest @pytest.mark.parametrize('fs', [[None, None, None, False]], indirect=True) def test_something(fs): ... patchfs ....... If you use the ``patchfs`` decorator, you can pass the arguments directly to the decorator: .. code:: python from pyfakefs.fake_filesystem_unittest import patchfs @patchfs(allow_root_user=False) def test_something(fake_fs): ... List of custom arguments ~~~~~~~~~~~~~~~~~~~~~~~~ Following is a description of the optional arguments that can be used to customize ``pyfakefs``. .. _modules_to_reload: modules_to_reload ................. ``Pyfakefs`` patches modules that are imported before starting the test by finding and replacing file system modules in all loaded modules at test initialization time. This allows to automatically patch file system related modules that are: - imported directly, for example: .. code:: python import os import pathlib.Path - imported as another name: .. code:: python import os as my_os - imported using one of these two specially handled statements: .. code:: python from os import path from pathlib import Path Additionally, functions from file system related modules are patched automatically if imported like: .. code:: python from os.path import exists from os import stat This also works if importing the functions as another name: .. code:: python from os.path import exists as my_exists from io import open as io_open from builtins import open as bltn_open There are a few cases where automatic patching does not work. We know of at least two specific cases where this is the case: Initializing a default argument with a file system function is not patched automatically due to performance reasons (though it can be switched on using :ref:`patch_default_args`): .. code:: python import os def check_if_exists(filepath, file_exists=os.path.exists): return file_exists(filepath) If initializing a global variable using a file system function, the initialization will be done using the real file system: .. code:: python from pathlib import Path path = Path("/example_home") In this case, ``path`` will hold the real file system path inside the test. The same is true, if a file system function is used in a decorator (this is an example from a related issue): .. code:: python import pathlib @click.command() @click.argument('foo', type=click.Path(path_type=pathlib.Path)) def hello(foo): pass To get these cases to work as expected under test, the respective modules containing the code shall be added to the ``modules_to_reload`` argument (a module list). The passed modules will be reloaded, thus allowing ``pyfakefs`` to patch them dynamically. All modules loaded after the initial patching described above will be patched using this second mechanism. Given that the example function ``check_if_exists`` shown above is located in the file ``example/sut.py``, the following code will work: .. code:: python import example # example using unittest class ReloadModuleTest(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs(modules_to_reload=[example.sut]) def test_path_exists(self): file_path = '/foo/bar' self.fs.create_dir(file_path) self.assertTrue(example.sut.check_if_exists(file_path)) # example using pytest @pytest.mark.parametrize('fs', [[None, [example.sut]]], indirect=True) def test_path_exists(fs): file_path = '/foo/bar' fs.create_dir(file_path) assert example.sut.check_if_exists(file_path) # example using Patcher def test_path_exists(): with Patcher(modules_to_reload=[example.sut]) as patcher: file_path = '/foo/bar' patcher.fs.create_dir(file_path) assert example.sut.check_if_exists(file_path) # example using patchfs decorator @patchfs(modules_to_reload=[example.sut]) def test_path_exists(fs): file_path = '/foo/bar' fs.create_dir(file_path) assert example.sut.check_if_exists(file_path) modules_to_patch ................ Sometimes there are file system modules in other packages that are not patched in standard ``pyfakefs``. To allow patching such modules, ``modules_to_patch`` can be used by adding a fake module implementation for a module name. The argument is a dictionary of fake modules mapped to the names to be faked. This mechanism is used in ``pyfakefs`` itself to patch the external modules `pathlib2` and `scandir` if present, and the following example shows how to fake a module in Django that uses OS file system functions (note that this has now been been integrated into ``pyfakefs``): .. code:: python class FakeLocks: """django.core.files.locks uses low level OS functions, fake it.""" _locks_module = django.core.files.locks def __init__(self, fs): """Each fake module expects the fake file system as an __init__ parameter.""" # fs represents the fake filesystem; for a real example, it can be # saved here and used in the implementation pass @staticmethod def lock(f, flags): return True @staticmethod def unlock(f): return True def __getattr__(self, name): return getattr(self._locks_module, name) ... # test code using Patcher with Patcher(modules_to_patch={'django.core.files.locks': FakeLocks}): test_django_stuff() # test code using unittest class TestUsingDjango(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs(modules_to_patch={'django.core.files.locks': FakeLocks}) def test_django_stuff(self) ... # test code using pytest @pytest.mark.parametrize('fs', [[None, None, {'django.core.files.locks': FakeLocks}]], indirect=True) def test_django_stuff(fs): ... # test code using patchfs decorator @patchfs(modules_to_patch={'django.core.files.locks': FakeLocks}) def test_django_stuff(fake_fs): ... additional_skip_names ..................... This may be used to add modules that shall not be patched. This is mostly used to avoid patching the Python file system modules themselves, but may be helpful in some special situations, for example if a testrunner needs to access the file system after test setup. To make this possible, the affected module can be added to ``additional_skip_names``: .. code:: python with Patcher(additional_skip_names=['pydevd']) as patcher: patcher.fs.create_file('foo') Alternatively to the module names, the modules themselves may be used: .. code:: python import pydevd with Patcher(additional_skip_names=[pydevd]) as patcher: patcher.fs.create_file('foo') .. _allow_root_user: allow_root_user ............... This is ``True`` by default, meaning that the user is considered a root user if the real user is a root user (e.g. has the user ID 0). If you want to run your tests as a non-root user regardless of the actual user rights, you may want to set this to ``False``. use_known_patches ................. Some libraries are known to require patching in order to work with ``pyfakefs``. If ``use_known_patches`` is set to ``True`` (the default), ``pyfakefs`` patches these libraries so that they will work with the fake filesystem. Currently, this includes patches for ``pandas`` read methods like ``read_csv`` and ``read_excel``, and for ``Django`` file locks--more may follow. Ordinarily, the default value of ``use_known_patches`` should be used, but it is present to allow users to disable this patching in case it causes any problems. It may be removed or replaced by more fine-grained arguments in future releases. patch_open_code ............... Since Python 3.8, the ``io`` module has the function ``open_code``, which opens a file read-only and is used to open Python code files. By default, this function is not patched, because the files it opens usually belong to the executed library code and are not present in the fake file system. Under some circumstances, this may not be the case, and the opened file lives in the fake filesystem. For these cases, you can set ``patch_open_code`` to ``PatchMode.ON``. If you just want to patch ``open_case`` for files that live in the fake filesystem, and use the real function for the rest, you can set ``patch_open_code`` to ``PatchMode.AUTO``: .. code:: python from pyfakefs.fake_filesystem_unittest import PatchMode @patchfs(patch_open_code=PatchMode.AUTO) def test_something(fs): ... .. note:: This argument is subject to change or removal in future versions of ``pyfakefs``, depending on the upcoming use cases. .. _patch_default_args: patch_default_args .................. As already mentioned, a default argument that is initialized with a file system function is not patched automatically: .. code:: python import os def check_if_exists(filepath, file_exists=os.path.exists): return file_exists(filepath) As this is rarely needed, and the check to patch this automatically is quite expansive, it is not done by default. Using ``patch_default_args`` will search for this kind of default arguments and patch them automatically. You could also use the :ref:`modules_to_reload` option with the module that contains the default argument instead, if you want to avoid the overhead. use_cache ......... If True (the default), patched and non-patched modules are cached between tests to avoid the performance hit of the file system function lookup (the patching itself is reverted after each test). As this is a new feature, this argument allows to turn it off in case it causes any problems: .. code:: python @patchfs(use_cache=False) def test_something(fake_fs): fake_fs.create_file("foo", contents="test") ... Please write an issue if you encounter any problem that can be fixed by using this parameter. Note that this argument may be removed in a later version, if no problems come up. If you want to clear the cache just for a specific test instead, you can call ``clear_cache`` on the ``Patcher`` or the ``fake_filesystem`` instance: .. code:: python def test_something(fs): # using pytest fixture fs.clear_cache() ... Using convenience methods ------------------------- While ``pyfakefs`` can be used just with the standard Python file system functions, there are few convenience methods in ``fake_filesystem`` that can help you setting up your tests. The methods can be accessed via the ``fake_filesystem`` instance in your tests: ``Patcher.fs``, the ``fs`` fixture in pytest, ``TestCase.fs`` for ``unittest``, and the ``fs`` argument for the ``patchfs`` decorator. File creation helpers ~~~~~~~~~~~~~~~~~~~~~ To create files, directories or symlinks together with all the directories in the path, you may use ``create_file()``, ``create_dir()``, ``create_symlink()`` and ``create_link()``, respectively. ``create_file()`` also allows you to set the file mode and the file contents together with the encoding if needed. Alternatively, you can define a file size without contents--in this case, you will not be able to perform standard I\O operations on the file (may be used to fill up the file system with large files, see also :ref:`set-fs-size`). .. code:: python from pyfakefs.fake_filesystem_unittest import TestCase class ExampleTestCase(TestCase): def setUp(self): self.setUpPyfakefs() def test_create_file(self): file_path = '/foo/bar/test.txt' self.fs.create_file(file_path, contents = 'test') with open(file_path) as f: self.assertEqual('test', f.read()) ``create_dir()`` behaves like ``os.makedirs()``. ``create_symlink`` and ``create_link`` behave like ``os.symlink`` and ``os.link``, with any missing parent directories of the link created automatically. .. caution:: The first two arguments in ``create_symlink`` are reverted in relation to ``os.symlink`` for historical reasons. Access to files in the real file system ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you want to have read access to real files or directories, you can map them into the fake file system using ``add_real_file()``, ``add_real_directory()``, ``add_real_symlink()`` and ``add_real_paths()``. They take a file path, a directory path, a symlink path, or a list of paths, respectively, and make them accessible from the fake file system. By default, the contents of the mapped files and directories are read only on demand, so that mapping them is relatively cheap. The access to the files is by default read-only, but even if you add them using ``read_only=False``, the files are written only in the fake system (e.g. in memory). The real files are never changed. ``add_real_file()``, ``add_real_directory()`` and ``add_real_symlink()`` also allow you to map a file or a directory tree into another location in the fake filesystem via the argument ``target_path``. .. code:: python from pyfakefs.fake_filesystem_unittest import TestCase class ExampleTestCase(TestCase): fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') def setUp(self): self.setUpPyfakefs() # make the file accessible in the fake file system self.fs.add_real_directory(self.fixture_path) def test_using_fixture1(self): with open(os.path.join(self.fixture_path, 'fixture1.txt') as f: # file contents are copied to the fake file system # only at this point contents = f.read() You can do the same using ``pytest`` by using a fixture for test setup: .. code:: python import pytest import os fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') @pytest.fixture def my_fs(fs): fs.add_real_directory(fixture_path) yield fs def test_using_fixture1(my_fs): with open(os.path.join(fixture_path, 'fixture1.txt') as f: contents = f.read() When using ``pytest`` another option is to load the contents of the real file in a fixture and pass this fixture to the test function **before** passing the ``fs`` fixture. .. code:: python import pytest import os @pytest.fixture def content(): fixture_path = os.path.join(os.path.dirname(__file__), 'fixtures') with open(os.path.join(fixture_path, 'fixture1.txt') as f: contents = f.read() return contents def test_using_file_contents(content, fs): fs.create_file("fake/path.txt") assert content != "" Handling mount points ~~~~~~~~~~~~~~~~~~~~~ Under Linux and MacOS, the root path (``/``) is the only mount point created in the fake file system. If you need support for more mount points, you can add them using ``add_mount_point()``. Under Windows, drives and UNC paths are internally handled as mount points. Adding a file or directory on another drive or UNC path automatically adds a mount point for that drive or UNC path root if needed. Explicitly adding mount points shall not be needed under Windows. A mount point has a separate device ID (``st_dev``) under all systems, and some operations (like ``rename``) are not possible for files located on different mount points. The fake file system size (if used) is also set per mount point. .. _set-fs-size: Setting the file system size ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ If you need to know the file system size in your tests (for example for testing cleanup scripts), you can set the fake file system size using ``set_disk_usage()``. By default, this sets the total size in bytes of the root partition; if you add a path as parameter, the size will be related to the mount point (see above) the path is related to. By default, the size of the fake file system is set to 1 TB (which for most tests can be considered as infinite). As soon as you set a size, all files will occupy the space according to their size, and you may fail to create new files if the fake file system is full. .. code:: python from pyfakefs.fake_filesystem_unittest import TestCase class ExampleTestCase(TestCase): def setUp(self): self.setUpPyfakefs() self.fs.set_disk_usage(100) def test_disk_full(self): with open('/foo/bar.txt', 'w') as f: with self.assertRaises(OSError): f.write('a' * 200) f.flush() To get the file system size, you may use ``get_disk_usage()``, which is modeled after ``shutil.disk_usage()``. Suspending patching ~~~~~~~~~~~~~~~~~~~ Sometimes, you may want to access the real filesystem inside the test with no patching applied. This can be achieved by using the ``pause/resume`` functions, which exist in ``fake_filesystem_unittest.Patcher``, ``fake_filesystem_unittest.TestCase`` and ``fake_filesystem.FakeFilesystem``. There is also a context manager class ``fake_filesystem_unittest.Pause`` which encapsulates the calls to ``pause()`` and ``resume()``. Here is an example that tests the usage with the ``pyfakefs`` pytest fixture: .. code:: python from pyfakefs.fake_filesystem_unittest import Pause def test_pause_resume_contextmanager(fs): fake_temp_file = tempfile.NamedTemporaryFile() assert os.path.exists(fake_temp_file.name) fs.pause() assert not os.path.exists(fake_temp_file.name) real_temp_file = tempfile.NamedTemporaryFile() assert os.path.exists(real_temp_file.name) fs.resume() assert not os.path.exists(real_temp_file.name) assert os.path.exists(fake_temp_file.name) Here is the same code using a context manager: .. code:: python from pyfakefs.fake_filesystem_unittest import Pause def test_pause_resume_contextmanager(fs): fake_temp_file = tempfile.NamedTemporaryFile() assert os.path.exists(fake_temp_file.name) with Pause(fs): assert not os.path.exists(fake_temp_file.name) real_temp_file = tempfile.NamedTemporaryFile() assert os.path.exists(real_temp_file.name) assert not os.path.exists(real_temp_file.name) assert os.path.exists(fake_temp_file.name) Simulating other file systems ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ``Pyfakefs`` supports Linux, MacOS and Windows operating systems. By default, the file system of the OS where the tests run is assumed, but it is possible to simulate other file systems to some extent. To set a specific file system, you can change ``pyfakefs.FakeFilesystem.os`` to one of ``OSType.LINUX``, ``OSType.MACOS`` and ``OSType.WINDOWS``. On doing so, the behavior of ``pyfakefs`` is adapted to the respective file system. Note that setting this causes the fake file system to be reset, so you should call it before adding any files. Setting the ``os`` attributes changes a number of ``pyfakefs.FakeFilesystem`` attributes, which can also be set separately if needed: - ``is_windows_fs`` - if ``True`` a Windows file system (NTFS) is assumed - ``is_macos`` - if ``True`` and ``is_windows_fs`` is ``False``, the standard MacOS file system (HFS+) is assumed - if ``is_windows_fs`` and ``is_macos`` are ``False``, a Linux file system (something like ext3) is assumed - ``is_case_sensitive`` is set to ``True`` under Linux and to ``False`` under Windows and MacOS by default - you can change it to change the respective behavior - ``path_separator`` is set to ``\`` under Windows and to ``/`` under Posix, ``alternative_path_separator`` is set to ``/`` under Windows and to ``None`` under Posix--these can also be adapted if needed The following test works both under Windows and Linux: .. code:: python from pyfakefs.fake_filesystem import OSType def test_windows_paths(fs): fs.os = OSType.WINDOWS assert r"C:\foo\bar" == os.path.join('C:\\', 'foo', 'bar')) assert os.path.splitdrive(r"C:\foo\bar") == ("C:", r"\foo\bar") assert os.path.ismount("C:") Troubleshooting --------------- Modules not working with pyfakefs ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Modules may not work with ``pyfakefs`` for several reasons. ``pyfakefs`` works by patching some file system related modules and functions, specifically: - most file system related functions in the ``os`` and ``os.path`` modules - the ``pathlib`` module - the build-in ``open`` function and ``io.open`` - ``shutil.disk_usage`` Other file system related modules work with ``pyfakefs``, because they use exclusively these patched functions, specifically ``shutil`` (except for ``disk_usage``), ``tempfile``, ``glob`` and ``zipfile``. A module may not work with ``pyfakefs`` because of one of the following reasons: - It uses a file system related function of the mentioned modules that is not or not correctly patched. Mostly these are functions that are seldom used, but may be used in Python libraries (this has happened for example with a changed implementation of ``shutil`` in Python 3.7). Generally, these shall be handled in issues and we are happy to fix them. - It uses file system related functions in a way that will not be patched automatically. This is the case for functions that are executed while reading a module. This case and a possibility to make them work is documented above under ``modules_to_reload``. - It uses OS specific file system functions not contained in the Python libraries. These will not work out of the box, and we generally will not support them in ``pyfakefs``. If these functions are used in isolated functions or classes, they may be patched by using the ``modules_to_patch`` parameter (see the example for file locks in Django above), or by using ``unittest.patch`` if you don't need to simulate the functions. We added some of these patches to ``pyfakefs``, so that they are applied automatically (currently done for some ``pandas`` and ``Django`` functionality). - It uses C libraries to access the file system. There is no way no make such a module work with ``pyfakefs``--if you want to use it, you have to patch the whole module. In some cases, a library implemented in Python with a similar interface already exists. An example is ``lxml``, which can be substituted with ``ElementTree`` in most cases for testing. A list of Python modules that are known to not work correctly with ``pyfakefs`` will be collected here: - `multiprocessing`_ has several issues (related to points 1 and 3 above). Currently there are no plans to fix this, but this may change in case of sufficient demand. - `subprocess`_ has very similar problems and cannot be used with ``pyfakefs`` to start a process. ``subprocess`` can either be mocked, if the process is not needed for the test, or patching can be paused to start a process if needed, and resumed afterwards (see `this issue `__). - Modules that rely on ``subprocess`` or ``multiprocessing`` to work correctly, e.g. need to start other executables. Examples that have shown this problem include `GitPython`_ and `plumbum`_. - the `Pillow`_ image library does not work with pyfakefs at least if writing JPEG files (see `this issue `__) - `pandas`_ (the Python data analysis library) uses its own internal file system access written in C. Thus much of ``pandas`` will not work with ``pyfakefs``. Having said that, ``pyfakefs`` patches ``pandas`` so that many of the ``read_xxx`` functions, including ``read_csv`` and ``read_excel``, as well as some writer functions, do work with the fake file system. If you use only these functions, ``pyfakefs`` will work with ``pandas``. If you are not sure if a module can be handled, or how to do it, you can always write a new issue, of course! Pyfakefs behaves differently than the real filesystem ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ There are basically two kinds of deviations from the actual behavior: - unwanted deviations that we didn't notice--if you find any of these, please write an issue and will try to fix it - behavior that depends on different OS versions and editions--as mentioned in :ref:`limitations`, ``pyfakefs`` uses the TravisCI systems as reference system and will not replicate all system-specific behavior OS temporary directories ~~~~~~~~~~~~~~~~~~~~~~~~ Tests relying on a completely empty file system on test start will fail. As ``pyfakefs`` does not fake the ``tempfile`` module (as described above), a temporary directory is required to ensure ``tempfile`` works correctly, e.g., that ``tempfile.gettempdir()`` will return a valid value. This means that any newly created fake file system will always have either a directory named ``/tmp`` when running on Linux or Unix systems, ``/var/folders//T`` when running on MacOs, or ``C:\Users\\AppData\Local\Temp`` on Windows. User rights ~~~~~~~~~~~ If you run ``pyfakefs`` tests as root (this happens by default if run in a docker container), ``pyfakefs`` also behaves as a root user, for example can write to write-protected files. This may not be the expected behavior, and can be changed. ``Pyfakefs`` has a rudimentary concept of user rights, which differentiates between root user (with the user id 0) and any other user. By default, ``pyfakefs`` assumes the user id of the current user, but you can change that using ``fake_filesystem.set_uid()`` in your setup. This allows to run tests as non-root user in a root user environment and vice verse. Another possibility to run tests as non-root user in a root user environment is the convenience argument :ref:`allow_root_user`. .. _usage_with_mock_open: Pyfakefs and mock_open ~~~~~~~~~~~~~~~~~~~~~~ If you patch ``open`` using ``mock_open`` before the initialization of ``pyfakefs``, it will not work properly, because the ``pyfakefs`` initialization relies on ``open`` working correctly. Generally, you should not need ``mock_open`` if using ``pyfakefs``, because you always can create the files with the needed content using ``create_file``. This is true for patching any filesystem functions - avoid patching them while working with ``pyfakefs``. If you still want to use ``mock_open``, make sure it is only used while patching is in progress. For example, if you are using ``pytest`` with the ``mocker`` fixture used to patch ``open``, make sure that the ``fs`` fixture is passed before the ``mocker`` fixture to ensure this. .. _`example.py`: https://github.com/jmcgeheeiv/pyfakefs/blob/master/pyfakefs/tests/example.py .. _`example_test.py`: https://github.com/jmcgeheeiv/pyfakefs/blob/master/pyfakefs/tests/example_test.py .. _`pytest`: https://doc.pytest.org .. _`nose`: https://docs.nose2.io/en/latest/ .. _`all Patcher arguments`: https://jmcgeheeiv.github.io/pyfakefs/master/modules.html#pyfakefs.fake_filesystem_unittest.Patcher .. _`multiprocessing`: https://docs.python.org/3/library/multiprocessing.html .. _`subprocess`: https://docs.python.org/3/library/subprocess.html .. _`GitPython`: https://pypi.org/project/GitPython/ .. _`plumbum`: https://pypi.org/project/plumbum/ .. _`Pillow`: https://pypi.org/project/Pillow/ .. _`pandas`: https://pypi.org/project/pandas/pyfakefs-4.5.4/extra_requirements.txt0000666000000000000000000000145514166355035016172 0ustar 00000000000000# "pathlib2" and "scandir" are backports of new standard modules, pyfakefs will # use them if available when running on older Python versions. # # They are dependencies of pytest when Python < 3.6 so we sometimes get them via # requirements.txt, this file makes them explicit dependencies for testing & # development. # # Older versions might work ok, the versions chosen here are just the latest # available at the time of writing. pathlib2>=2.3.2 scandir>=1.8 # pandas + xlrd are used to test pandas-specific patches to allow # pyfakefs to work with pandas # we use the latest version to see any problems with new versions pandas==1.1.5; python_version <= '3.6' pandas==1.3.5; python_version > '3.6' xlrd==1.2.0; python_version <= '3.6' xlrd==2.0.1; python_version > '3.6' openpyxl==3.0.9 pyfakefs-4.5.4/MANIFEST.in0000666000000000000000000000007013757263357013243 0ustar 00000000000000include CHANGES.md include COPYING include README.md pyfakefs-4.5.4/mypy.ini0000666000000000000000000000104514147521400013163 0ustar 00000000000000[mypy] show_error_codes = True warn_unused_configs = True exclude=(docs|pyfakefs/tests) [mypy-django.*] ignore_missing_imports = True [mypy-hotshot.*] ignore_missing_imports = True [mypy-openpyxl.*] ignore_missing_imports = True [mypy-pandas.*] ignore_missing_imports = True [mypy-pathlib2.*] ignore_missing_imports = True [mypy-psyco.*] ignore_missing_imports = True [mypy-scandir.*] ignore_missing_imports = True [mypy-setuptools.*] ignore_missing_imports = True [mypy-xlrd.*] ignore_missing_imports = True pyfakefs-4.5.4/PKG-INFO0000666000000000000000000001743514167600566012610 0ustar 00000000000000Metadata-Version: 2.1 Name: pyfakefs Version: 4.5.4 Summary: pyfakefs implements a fake file system that mocks the Python file system modules. Home-page: http://pyfakefs.org Author: Google Author-email: google-pyfakefs@google.com Maintainer: John McGehee Maintainer-email: pyfakefs@johnnado.com License: http://www.apache.org/licenses/LICENSE-2.0 Description: # 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/jmcgeheeiv/pyfakefs/workflows/Testsuite/badge.svg) pyfakefs implements a fake file system that mocks the Python file system modules. Using pyfakefs, your tests operate on a fake file system in memory without touching the real disk. The software under test requires no modification to work with pyfakefs. pyfakefs works with Linux, Windows and MacOS. ## Documentation This file provides general usage instructions for pyfakefs. There is more: * The documentation at [GitHub Pages:](http://jmcgeheeiv.github.io/pyfakefs) * The [Release documentation](http://jmcgeheeiv.github.io/pyfakefs/release) contains usage documentation for pyfakefs and a description of the most relevant classes, methods and functions for the last version released on PyPi * The [Development documentation](http://jmcgeheeiv.github.io/pyfakefs/master) contains the same documentation for the current master branch * The [Release 3.7 documentation](http://jmcgeheeiv.github.io/pyfakefs/release37) contains usage documentation for the last version of pyfakefs supporting Python 2.7 * The [Release 3.3 documentation](http://jmcgeheeiv.github.io/pyfakefs/release33) contains usage documentation for the last version of pyfakefs supporting Python 2.6, and for the old-style API (which is still supported but not documented in the current release) * The [Release Notes](https://github.com/jmcgeheeiv/pyfakefs/blob/master/CHANGES.md) show a list of changes in the latest versions ### Linking to pyfakefs In your own documentation, please link to pyfakefs using the canonical URL . This URL always points to the most relevant top page for pyfakefs. ## Usage pyfakefs has support for `unittest` and `pytest`, but can also be used directly using `fake_filesystem_unittest.Patcher`. Refer to the [usage documentation](http://jmcgeheeiv.github.io/pyfakefs/master/usage.html) for more information on test scenarios, test customization and using convenience functions. ## Compatibility pyfakefs works with CPython 3.6 and above, on Linux, Windows and OSX (MacOS), and with PyPy3. pyfakefs works with [PyTest](http://doc.pytest.org) version 2.8.6 or above. pyfakefs will not work with Python libraries that use C libraries to access the file system. This is because pyfakefs cannot patch the underlying C libraries' file access functions--the C libraries will always access the real file system. For example, pyfakefs will not work with [`lxml`](http://lxml.de/). In this case `lxml` must be replaced with a pure Python alternative such as [`xml.etree.ElementTree`](https://docs.python.org/3/library/xml.etree.elementtree.html). ## Development ### Continuous integration pyfakefs is currently automatically tested on Linux, MacOS and Windows, with Python 3.6 to 3.10, and with PyPy3 on Linux, using [GitHub Actions](https://github.com/jmcgeheeiv/pyfakefs/actions). ### Running pyfakefs unit tests #### On the command line pyfakefs unit tests can be run using `unittest` or `pytest`: ```bash $ cd pyfakefs/ $ export PYTHONPATH=$PWD $ python -m pyfakefs.tests.all_tests $ python -m pyfakefs.tests.all_tests_without_extra_packages $ python -m pytest pyfakefs/pytest_tests/pytest_plugin_test.py ``` These scripts are called by `tox` and Travis-CI. `tox` can be used to run tests locally against supported python versions: ```bash $ tox ``` #### In a Docker container The `Dockerfile` at the repository root will run the tests on the latest Ubuntu version. Build the container: ```bash cd pyfakefs/ docker build -t pyfakefs . ``` Run the unit tests in the container: ```bash docker run -t pyfakefs ``` ### Contributing to pyfakefs We always welcome contributions to the library. Check out the [Contributing Guide](https://github.com/jmcgeheeiv/pyfakefs/blob/master/CONTRIBUTING.md) for more information. ## History pyfakefs.py was initially developed at Google by Mike Bland as a modest fake implementation of core Python modules. It was introduced to all of Google in September 2006. Since then, it has been enhanced to extend its functionality and usefulness. At last count, pyfakefs is used in over 2,000 Python tests at Google. Google released pyfakefs to the public in 2011 as Google Code project [pyfakefs](http://code.google.com/p/pyfakefs/): * Fork [jmcgeheeiv-pyfakefs](http://code.google.com/p/jmcgeheeiv-pyfakefs/) added [direct support for unittest and doctest](../../wiki/Automatically-find-and-patch-file-functions-and-modules) * Fork [shiffdane-jmcgeheeiv-pyfakefs](http://code.google.com/p/shiffdane-jmcgeheeiv-pyfakefs/) added further corrections After the [shutdown of Google Code](http://google-opensource.blogspot.com/2015/03/farewell-to-google-code.html) was announced, [John McGehee](https://github.com/jmcgeheeiv) merged all three Google Code projects together [here on GitHub](https://github.com/jmcgeheeiv/pyfakefs) where an enthusiastic community actively supports, maintains and extends pyfakefs. Keywords: testing,test,file,os,shutil,glob,mocking,unittest,fakes,filesystem,unit Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Operating System :: POSIX Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Testing Classifier: Topic :: System :: Filesystems Classifier: Framework :: Pytest Requires-Python: >=3.6 Description-Content-Type: text/markdown pyfakefs-4.5.4/pyfakefs/0000777000000000000000000000000014167600566013311 5ustar 00000000000000pyfakefs-4.5.4/pyfakefs/deprecator.py0000666000000000000000000000501613757263357016024 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Utilities for handling deprecated functions.""" import functools import warnings class Deprecator(object): """Decorator class for adding deprecated functions. Warnings are switched on by default. To disable deprecation warnings, use: >>> from pyfakefs.deprecator import Deprecator >>> >>> Deprecator.show_warnings = False """ show_warnings = True def __init__(self, use_instead=None, func_name=None): self.use_instead = use_instead self.func_name = func_name def __call__(self, func): """Decorator to mark functions as deprecated. Emit warning when the function is used.""" @functools.wraps(func) def _new_func(*args, **kwargs): if self.show_warnings: with warnings.catch_warnings(): warnings.simplefilter('always', DeprecationWarning) message = '' if self.use_instead is not None: message = 'Use {} instead.'.format(self.use_instead) warnings.warn('Call to deprecated function {}. {}'.format( self.func_name or func.__name__, message), category=DeprecationWarning, stacklevel=2) return func(*args, **kwargs) return _new_func @staticmethod def add(clss, func, deprecated_name): """Add the deprecated version of a member function to the given class. Gives a deprecation warning on usage. Args: clss: the class where the deprecated function is to be added func: the actual function that is called by the deprecated version deprecated_name: the deprecated name of the function """ @Deprecator(func.__name__, deprecated_name) def _old_function(*args, **kwargs): return func(*args, **kwargs) setattr(clss, deprecated_name, _old_function) pyfakefs-4.5.4/pyfakefs/extra_packages.py0000666000000000000000000000224214147521400016627 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Imports external packages that replace or emulate internal packages. If the external module is not present, the build-in module is imported. """ try: import pathlib2 except ImportError: pathlib2 = None try: import scandir use_scandir_package = True use_builtin_scandir = False except ImportError: try: from os import scandir # noqa: F401 use_builtin_scandir = True use_scandir_package = False except ImportError: use_builtin_scandir = False use_scandir_package = False use_scandir = use_scandir_package or use_builtin_scandir pyfakefs-4.5.4/pyfakefs/fake_filesystem.py0000666000000000000000000071400514166355054017042 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A fake filesystem implementation for unit testing. :Includes: * :py:class:`FakeFile`: Provides the appearance of a real file. * :py:class:`FakeDirectory`: Provides the appearance of a real directory. * :py:class:`FakeFilesystem`: Provides the appearance of a real directory hierarchy. * :py:class:`FakeOsModule`: Uses :py:class:`FakeFilesystem` to provide a fake :py:mod:`os` module replacement. * :py:class:`FakeIoModule`: Uses :py:class:`FakeFilesystem` to provide a fake ``io`` module replacement. * :py:class:`FakePathModule`: Faked ``os.path`` module replacement. * :py:class:`FakeFileOpen`: Faked ``file()`` and ``open()`` function replacements. :Usage: >>> from pyfakefs import fake_filesystem >>> filesystem = fake_filesystem.FakeFilesystem() >>> os_module = fake_filesystem.FakeOsModule(filesystem) >>> pathname = '/a/new/dir/new-file' Create a new file object, creating parent directory objects as needed: >>> os_module.path.exists(pathname) False >>> new_file = filesystem.create_file(pathname) File objects can't be overwritten: >>> os_module.path.exists(pathname) True >>> try: ... filesystem.create_file(pathname) ... except OSError as e: ... assert e.errno == errno.EEXIST, 'unexpected errno: %d' % e.errno ... assert e.strerror == 'File exists in the fake filesystem' Remove a file object: >>> filesystem.remove_object(pathname) >>> os_module.path.exists(pathname) False Create a new file object at the previous path: >>> beatles_file = filesystem.create_file(pathname, ... contents='Dear Prudence\\nWon\\'t you come out to play?\\n') >>> os_module.path.exists(pathname) True Use the FakeFileOpen class to read fake file objects: >>> file_module = fake_filesystem.FakeFileOpen(filesystem) >>> for line in file_module(pathname): ... print(line.rstrip()) ... Dear Prudence Won't you come out to play? File objects cannot be treated like directory objects: >>> try: ... os_module.listdir(pathname) ... except OSError as e: ... assert e.errno == errno.ENOTDIR, 'unexpected errno: %d' % e.errno ... assert e.strerror == 'Not a directory in the fake filesystem' The FakeOsModule can list fake directory objects: >>> os_module.listdir(os_module.path.dirname(pathname)) ['new-file'] The FakeOsModule also supports stat operations: >>> import stat >>> stat.S_ISREG(os_module.stat(pathname).st_mode) True >>> stat.S_ISDIR(os_module.stat(os_module.path.dirname(pathname)).st_mode) True """ import errno import heapq import io import locale import os import random import sys import traceback import uuid from collections import namedtuple from doctest import TestResults from enum import Enum from stat import ( S_IFREG, S_IFDIR, S_ISLNK, S_IFMT, S_ISDIR, S_IFLNK, S_ISREG, S_IFSOCK ) from types import ModuleType, TracebackType from typing import ( List, Optional, Callable, Union, Any, Dict, Tuple, cast, AnyStr, overload, NoReturn, ClassVar, IO, Iterator, TextIO, Type ) from pyfakefs.deprecator import Deprecator from pyfakefs.extra_packages import use_scandir from pyfakefs.fake_scandir import scandir, walk, ScanDirIter from pyfakefs.helpers import ( FakeStatResult, BinaryBufferIO, TextBufferIO, is_int_type, is_byte_string, is_unicode_string, make_string_path, IS_PYPY, to_string, matching_string, real_encoding, now, AnyPath, to_bytes ) from pyfakefs import __version__ # noqa: F401 for upwards compatibility PERM_READ = 0o400 # Read permission bit. PERM_WRITE = 0o200 # Write permission bit. PERM_EXE = 0o100 # Execute permission bit. PERM_DEF = 0o777 # Default permission bits. PERM_DEF_FILE = 0o666 # Default permission bits (regular file) PERM_ALL = 0o7777 # All permission bits. _OpenModes = namedtuple( '_OpenModes', 'must_exist can_read can_write truncate append must_not_exist' ) _OPEN_MODE_MAP = { # mode name:(file must exist, can read, can write, # truncate, append, must not exist) 'r': (True, True, False, False, False, False), 'w': (False, False, True, True, False, False), 'a': (False, False, True, False, True, False), 'r+': (True, True, True, False, False, False), 'w+': (False, True, True, True, False, False), 'a+': (False, True, True, False, True, False), 'x': (False, False, True, False, False, True), 'x+': (False, True, True, False, False, True) } AnyFileWrapper = Union[ "FakeFileWrapper", "FakeDirWrapper", "StandardStreamWrapper", "FakePipeWrapper" ] AnyString = Union[str, bytes] AnyFile = Union["FakeFile", "FakeDirectory"] if sys.platform.startswith('linux'): # on newer Linux system, the default maximum recursion depth is 40 # we ignore older systems here _MAX_LINK_DEPTH = 40 else: # on MacOS and Windows, the maximum recursion depth is 32 _MAX_LINK_DEPTH = 32 NR_STD_STREAMS = 3 if sys.platform == 'win32': USER_ID = 1 GROUP_ID = 1 else: USER_ID = os.getuid() GROUP_ID = os.getgid() class OSType(Enum): """Defines the real or simulated OS of the underlying file system.""" LINUX = "linux" MACOS = "macos" WINDOWS = "windows" class PatchMode(Enum): """Defines if patching shall be on, off, or in automatic mode. Currently only used for `patch_open_code` option. """ OFF = 1 AUTO = 2 ON = 3 def set_uid(uid: int) -> None: """Set the global user id. This is used as st_uid for new files and to differentiate between a normal user and the root user (uid 0). For the root user, some permission restrictions are ignored. Args: uid: (int) the user ID of the user calling the file system functions. """ global USER_ID USER_ID = uid def set_gid(gid: int) -> None: """Set the global group id. This is only used to set st_gid for new files, no permision checks are performed. Args: gid: (int) the group ID of the user calling the file system functions. """ global GROUP_ID GROUP_ID = gid def reset_ids() -> None: """Set the global user ID and group ID back to default values.""" if sys.platform == 'win32': set_uid(1) set_gid(1) else: set_uid(os.getuid()) set_gid(os.getgid()) def is_root() -> bool: """Return True if the current user is the root user.""" return USER_ID == 0 class FakeLargeFileIoException(Exception): """Exception thrown on unsupported operations for fake large files. Fake large files have a size with no real content. """ def __init__(self, file_path: str) -> None: super(FakeLargeFileIoException, self).__init__( 'Read and write operations not supported for ' 'fake large file: %s' % file_path) def _copy_module(old: ModuleType) -> ModuleType: """Recompiles and creates new module object.""" saved = sys.modules.pop(old.__name__, None) new = __import__(old.__name__) if saved is not None: sys.modules[old.__name__] = saved return new class FakeFile: """Provides the appearance of a real file. Attributes currently faked out: * `st_mode`: user-specified, otherwise S_IFREG * `st_ctime`: the time.time() timestamp of the file change time (updated each time a file's attributes is modified). * `st_atime`: the time.time() timestamp when the file was last accessed. * `st_mtime`: the time.time() timestamp when the file was last modified. * `st_size`: the size of the file * `st_nlink`: the number of hard links to the file * `st_ino`: the inode number - a unique number identifying the file * `st_dev`: a unique number identifying the (fake) file system device the file belongs to * `st_uid`: always set to USER_ID, which can be changed globally using `set_uid` * `st_gid`: always set to GROUP_ID, which can be changed globally using `set_gid` .. note:: The resolution for `st_ctime`, `st_mtime` and `st_atime` in the real file system depends on the used file system (for example it is only 1s for HFS+ and older Linux file systems, but much higher for ext4 and NTFS). This is currently ignored by pyfakefs, which uses the resolution of `time.time()`. Under Windows, `st_atime` is not updated for performance reasons by default. pyfakefs never updates `st_atime` under Windows, assuming the default setting. """ stat_types = ( 'st_mode', 'st_ino', 'st_dev', 'st_nlink', 'st_uid', 'st_gid', 'st_size', 'st_atime', 'st_mtime', 'st_ctime', 'st_atime_ns', 'st_mtime_ns', 'st_ctime_ns' ) def __init__(self, name: AnyStr, st_mode: int = S_IFREG | PERM_DEF_FILE, contents: Optional[AnyStr] = None, filesystem: Optional["FakeFilesystem"] = None, encoding: Optional[str] = None, errors: Optional[str] = None, side_effect: Optional[Callable[["FakeFile"], None]] = None): """ Args: name: Name of the file/directory, without parent path information st_mode: The stat.S_IF* constant representing the file type (i.e. stat.S_IFREG, stat.S_IFDIR), and the file permissions. If no file type is set (e.g. permission flags only), a regular file type is assumed. contents: The contents of the filesystem object; should be a string or byte object for regular files, and a dict of other FakeFile or FakeDirectory objects wih the file names as keys for FakeDirectory objects filesystem: The fake filesystem where the file is created. encoding: If contents is a unicode string, the encoding used for serialization. errors: The error mode used for encoding/decoding errors. side_effect: function handle that is executed when file is written, must accept the file object as an argument. """ # to be backwards compatible regarding argument order, we raise on None if filesystem is None: raise ValueError('filesystem shall not be None') self.filesystem: FakeFilesystem = filesystem self._side_effect: Optional[Callable] = side_effect self.name: AnyStr = name # type: ignore[assignment] self.stat_result = FakeStatResult( filesystem.is_windows_fs, USER_ID, GROUP_ID, now()) if st_mode >> 12 == 0: st_mode |= S_IFREG self.stat_result.st_mode = st_mode self.st_size: int = 0 self.encoding: Optional[str] = real_encoding(encoding) self.errors: str = errors or 'strict' self._byte_contents: Optional[bytes] = self._encode_contents(contents) self.stat_result.st_size = ( len(self._byte_contents) if self._byte_contents is not None else 0) self.epoch: int = 0 self.parent_dir: Optional[FakeDirectory] = None # Linux specific: extended file system attributes self.xattr: Dict = {} self.opened_as: AnyString = '' @property def byte_contents(self) -> Optional[bytes]: """Return the contents as raw byte array.""" return self._byte_contents @property def contents(self) -> Optional[str]: """Return the contents as string with the original encoding.""" if isinstance(self.byte_contents, bytes): return self.byte_contents.decode( self.encoding or locale.getpreferredencoding(False), errors=self.errors) return None @property def st_ctime(self) -> float: """Return the creation time of the fake file.""" return self.stat_result.st_ctime @st_ctime.setter def st_ctime(self, val: float) -> None: """Set the creation time of the fake file.""" self.stat_result.st_ctime = val @property def st_atime(self) -> float: """Return the access time of the fake file.""" return self.stat_result.st_atime @st_atime.setter def st_atime(self, val: float) -> None: """Set the access time of the fake file.""" self.stat_result.st_atime = val @property def st_mtime(self) -> float: """Return the modification time of the fake file.""" return self.stat_result.st_mtime @st_mtime.setter def st_mtime(self, val: float) -> None: """Set the modification time of the fake file.""" self.stat_result.st_mtime = val def set_large_file_size(self, st_size: int) -> None: """Sets the self.st_size attribute and replaces self.content with None. Provided specifically to simulate very large files without regards to their content (which wouldn't fit in memory). Note that read/write operations with such a file raise :py:class:`FakeLargeFileIoException`. Args: st_size: (int) The desired file size Raises: OSError: if the st_size is not a non-negative integer, or if st_size exceeds the available file system space """ self._check_positive_int(st_size) if self.st_size: self.size = 0 if self.filesystem: self.filesystem.change_disk_usage(st_size, self.name, self.st_dev) self.st_size = st_size self._byte_contents = None def _check_positive_int(self, size: int) -> None: # the size should be an positive integer value if not is_int_type(size) or size < 0: self.filesystem.raise_os_error(errno.ENOSPC, self.name) def is_large_file(self) -> bool: """Return `True` if this file was initialized with size but no contents. """ return self._byte_contents is None def _encode_contents( self, contents: Union[str, bytes, None]) -> Optional[bytes]: if is_unicode_string(contents): contents = bytes( cast(str, contents), self.encoding or locale.getpreferredencoding(False), self.errors) return cast(bytes, contents) def set_initial_contents(self, contents: AnyStr) -> bool: """Sets the file contents and size. Called internally after initial file creation. Args: contents: string, new content of file. Returns: True if the contents have been changed. Raises: OSError: if the st_size is not a non-negative integer, or if st_size exceeds the available file system space """ byte_contents = self._encode_contents(contents) changed = self._byte_contents != byte_contents st_size = len(byte_contents) if byte_contents else 0 current_size = self.st_size or 0 self.filesystem.change_disk_usage( st_size - current_size, self.name, self.st_dev) if self._byte_contents: self.size = 0 self._byte_contents = byte_contents self.st_size = st_size self.epoch += 1 return changed def set_contents(self, contents: AnyStr, encoding: Optional[str] = None) -> bool: """Sets the file contents and size and increases the modification time. Also executes the side_effects if available. Args: contents: (str, bytes) new content of file. encoding: (str) the encoding to be used for writing the contents if they are a unicode string. If not given, the locale preferred encoding is used. Raises: OSError: if `st_size` is not a non-negative integer, or if it exceeds the available file system space. """ self.encoding = real_encoding(encoding) changed = self.set_initial_contents(contents) if self._side_effect is not None: self._side_effect(self) return changed @property def size(self) -> int: """Return the size in bytes of the file contents. """ return self.st_size @size.setter def size(self, st_size: int) -> None: """Resizes file content, padding with nulls if new size exceeds the old size. Args: st_size: The desired size for the file. Raises: OSError: if the st_size arg is not a non-negative integer or if st_size exceeds the available file system space """ self._check_positive_int(st_size) current_size = self.st_size or 0 self.filesystem.change_disk_usage( st_size - current_size, self.name, self.st_dev) if self._byte_contents: if st_size < current_size: self._byte_contents = self._byte_contents[:st_size] else: self._byte_contents += b'\0' * (st_size - current_size) self.st_size = st_size self.epoch += 1 @property def path(self) -> AnyStr: """Return the full path of the current object.""" names: List[AnyStr] = [] obj: Optional[FakeFile] = self while obj: names.insert( 0, matching_string(self.name, obj.name)) # type: ignore obj = obj.parent_dir sep = self.filesystem.get_path_separator(names[0]) if names[0] == sep: names.pop(0) dir_path = sep.join(names) drive = self.filesystem.splitdrive(dir_path)[0] # if a Windows path already starts with a drive or UNC path, # no extra separator is needed if not drive: dir_path = sep + dir_path else: dir_path = sep.join(names) return self.filesystem.absnormpath(dir_path) @Deprecator('property path') def GetPath(self): return self.path @Deprecator('property size') def GetSize(self): return self.size @Deprecator('property size') def SetSize(self, value): self.size = value @Deprecator('property st_atime') def SetATime(self, st_atime): """Set the self.st_atime attribute. Args: st_atime: The desired access time. """ self.st_atime = st_atime @Deprecator('property st_mtime') def SetMTime(self, st_mtime): """Set the self.st_mtime attribute. Args: st_mtime: The desired modification time. """ self.st_mtime = st_mtime @Deprecator('property st_ctime') def SetCTime(self, st_ctime): """Set the self.st_ctime attribute. Args: st_ctime: The desired creation time. """ self.st_ctime = st_ctime def __getattr__(self, item: str) -> Any: """Forward some properties to stat_result.""" if item in self.stat_types: return getattr(self.stat_result, item) return super().__getattribute__(item) def __setattr__(self, key: str, value: Any) -> None: """Forward some properties to stat_result.""" if key in self.stat_types: return setattr(self.stat_result, key, value) return super().__setattr__(key, value) def __str__(self) -> str: return '%r(%o)' % (self.name, self.st_mode) @Deprecator('st_ino') def SetIno(self, st_ino): """Set the self.st_ino attribute. Note that a unique inode is assigned automatically to a new fake file. This function does not guarantee uniqueness and should be used with caution. Args: st_ino: (int) The desired inode. """ self.st_ino = st_ino class FakeNullFile(FakeFile): def __init__(self, filesystem: "FakeFilesystem") -> None: devnull = 'nul' if filesystem.is_windows_fs else '/dev/null' super(FakeNullFile, self).__init__( devnull, filesystem=filesystem, contents='') @property def byte_contents(self) -> bytes: return b'' def set_initial_contents(self, contents: AnyStr) -> bool: return False Deprecator.add(FakeFile, FakeFile.set_large_file_size, 'SetLargeFileSize') Deprecator.add(FakeFile, FakeFile.set_contents, 'SetContents') Deprecator.add(FakeFile, FakeFile.is_large_file, 'IsLargeFile') class FakeFileFromRealFile(FakeFile): """Represents a fake file copied from the real file system. The contents of the file are read on demand only. """ def __init__(self, file_path: str, filesystem: "FakeFilesystem", side_effect: Optional[Callable] = None) -> None: """ Args: file_path: Path to the existing file. filesystem: The fake filesystem where the file is created. Raises: OSError: if the file does not exist in the real file system. OSError: if the file already exists in the fake file system. """ super().__init__( name=os.path.basename(file_path), filesystem=filesystem, side_effect=side_effect) self.contents_read = False @property def byte_contents(self) -> Optional[bytes]: if not self.contents_read: self.contents_read = True with io.open(self.file_path, 'rb') as f: self._byte_contents = f.read() # On MacOS and BSD, the above io.open() updates atime on the real file self.st_atime = os.stat(self.file_path).st_atime return self._byte_contents def set_contents(self, contents, encoding=None): self.contents_read = True super(FakeFileFromRealFile, self).set_contents(contents, encoding) def is_large_file(self): """The contents are never faked.""" return False class FakeDirectory(FakeFile): """Provides the appearance of a real directory.""" def __init__(self, name: str, perm_bits: int = PERM_DEF, filesystem: Optional["FakeFilesystem"] = None): """ Args: name: name of the file/directory, without parent path information perm_bits: permission bits. defaults to 0o777. filesystem: if set, the fake filesystem where the directory is created """ FakeFile.__init__( self, name, S_IFDIR | perm_bits, '', filesystem=filesystem) # directories have the link count of contained entries, # including '.' and '..' self.st_nlink += 1 self._entries: Dict[str, AnyFile] = {} def set_contents(self, contents: AnyStr, encoding: Optional[str] = None) -> bool: raise self.filesystem.raise_os_error(errno.EISDIR, self.path) @property def entries(self) -> Dict[str, FakeFile]: """Return the list of contained directory entries.""" return self._entries @property def ordered_dirs(self) -> List[str]: """Return the list of contained directory entry names ordered by creation order. """ return [item[0] for item in sorted( self._entries.items(), key=lambda entry: entry[1].st_ino)] def add_entry(self, path_object: FakeFile) -> None: """Adds a child FakeFile to this directory. Args: path_object: FakeFile instance to add as a child of this directory. Raises: OSError: if the directory has no write permission (Posix only) OSError: if the file or directory to be added already exists """ if (not is_root() and not self.st_mode & PERM_WRITE and not self.filesystem.is_windows_fs): raise OSError(errno.EACCES, 'Permission Denied', self.path) path_object_name: str = to_string(path_object.name) if path_object_name in self.entries: self.filesystem.raise_os_error(errno.EEXIST, self.path) self._entries[path_object_name] = path_object path_object.parent_dir = self if path_object.st_ino is None: self.filesystem.last_ino += 1 path_object.st_ino = self.filesystem.last_ino self.st_nlink += 1 path_object.st_nlink += 1 path_object.st_dev = self.st_dev if path_object.st_nlink == 1: self.filesystem.change_disk_usage( path_object.size, path_object.name, self.st_dev) def get_entry(self, pathname_name: str) -> AnyFile: """Retrieves the specified child file or directory entry. Args: pathname_name: The basename of the child object to retrieve. Returns: The fake file or directory object. Raises: KeyError: if no child exists by the specified name. """ pathname_name = self._normalized_entryname(pathname_name) return self.entries[to_string(pathname_name)] def _normalized_entryname(self, pathname_name: str) -> str: if not self.filesystem.is_case_sensitive: matching_names = [name for name in self.entries if name.lower() == pathname_name.lower()] if matching_names: pathname_name = matching_names[0] return pathname_name def remove_entry(self, pathname_name: str, recursive: bool = True) -> None: """Removes the specified child file or directory. Args: pathname_name: Basename of the child object to remove. recursive: If True (default), the entries in contained directories are deleted first. Used to propagate removal errors (e.g. permission problems) from contained entries. Raises: KeyError: if no child exists by the specified name. OSError: if user lacks permission to delete the file, or (Windows only) the file is open. """ pathname_name = self._normalized_entryname(pathname_name) entry = self.get_entry(pathname_name) if self.filesystem.is_windows_fs: if entry.st_mode & PERM_WRITE == 0: self.filesystem.raise_os_error(errno.EACCES, pathname_name) if self.filesystem.has_open_file(entry): self.filesystem.raise_os_error(errno.EACCES, pathname_name) else: if (not is_root() and (self.st_mode & (PERM_WRITE | PERM_EXE) != PERM_WRITE | PERM_EXE)): self.filesystem.raise_os_error(errno.EACCES, pathname_name) if recursive and isinstance(entry, FakeDirectory): while entry.entries: entry.remove_entry(list(entry.entries)[0]) elif entry.st_nlink == 1: self.filesystem.change_disk_usage( -entry.size, pathname_name, entry.st_dev) self.st_nlink -= 1 entry.st_nlink -= 1 assert entry.st_nlink >= 0 del self.entries[to_string(pathname_name)] @property def size(self) -> int: """Return the total size of all files contained in this directory tree. """ return sum([item[1].size for item in self.entries.items()]) @size.setter def size(self, st_size: int) -> None: """Setting the size is an error for a directory.""" raise self.filesystem.raise_os_error(errno.EISDIR, self.path) @Deprecator('property size') def GetSize(self): return self.size def has_parent_object(self, dir_object: "FakeDirectory") -> bool: """Return `True` if dir_object is a direct or indirect parent directory, or if both are the same object.""" obj: Optional[FakeDirectory] = self while obj: if obj == dir_object: return True obj = obj.parent_dir return False def __str__(self) -> str: description = super(FakeDirectory, self).__str__() + ':\n' for item in self.entries: item_desc = self.entries[item].__str__() for line in item_desc.split('\n'): if line: description = description + ' ' + line + '\n' return description Deprecator.add(FakeDirectory, FakeDirectory.add_entry, 'AddEntry') Deprecator.add(FakeDirectory, FakeDirectory.get_entry, 'GetEntry') Deprecator.add(FakeDirectory, FakeDirectory.set_contents, 'SetContents') Deprecator.add(FakeDirectory, FakeDirectory.remove_entry, 'RemoveEntry') class FakeDirectoryFromRealDirectory(FakeDirectory): """Represents a fake directory copied from the real file system. The contents of the directory are read on demand only. """ def __init__(self, source_path: AnyPath, filesystem: "FakeFilesystem", read_only: bool, target_path: Optional[AnyPath] = None): """ Args: source_path: Full directory path. filesystem: The fake filesystem where the directory is created. read_only: If set, all files under the directory are treated as read-only, e.g. a write access raises an exception; otherwise, writing to the files changes the fake files only as usually. target_path: If given, the target path of the directory, otherwise the target is the same as `source_path`. Raises: OSError: if the directory does not exist in the real file system """ target_path = target_path or source_path real_stat = os.stat(source_path) super(FakeDirectoryFromRealDirectory, self).__init__( name=to_string(os.path.split(target_path)[1]), perm_bits=real_stat.st_mode, filesystem=filesystem) self.st_ctime = real_stat.st_ctime self.st_atime = real_stat.st_atime self.st_mtime = real_stat.st_mtime self.st_gid = real_stat.st_gid self.st_uid = real_stat.st_uid self.source_path = source_path # type: ignore self.read_only = read_only self.contents_read = False @property def entries(self) -> Dict[str, FakeFile]: """Return the list of contained directory entries, loading them if not already loaded.""" if not self.contents_read: self.contents_read = True base = self.path for entry in os.listdir(self.source_path): source_path = os.path.join(self.source_path, entry) target_path = os.path.join(base, entry) # type: ignore if os.path.islink(source_path): self.filesystem.add_real_symlink(source_path, target_path) elif os.path.isdir(source_path): self.filesystem.add_real_directory( source_path, self.read_only, target_path=target_path) else: self.filesystem.add_real_file( source_path, self.read_only, target_path=target_path) return self._entries @property def size(self) -> int: # we cannot get the size until the contents are loaded if not self.contents_read: return 0 return super(FakeDirectoryFromRealDirectory, self).size @size.setter def size(self, st_size: int) -> None: raise self.filesystem.raise_os_error(errno.EISDIR, self.path) class FakeFilesystem: """Provides the appearance of a real directory tree for unit testing. Attributes: path_separator: The path separator, corresponds to `os.path.sep`. alternative_path_separator: Corresponds to `os.path.altsep`. is_windows_fs: `True` in a real or faked Windows file system. is_macos: `True` under MacOS, or if we are faking it. is_case_sensitive: `True` if a case-sensitive file system is assumed. root: The root :py:class:`FakeDirectory` entry of the file system. cwd: The current working directory path. umask: The umask used for newly created files, see `os.umask`. patcher: Holds the Patcher object if created from it. Allows access to the patcher object if using the pytest fs fixture. """ def __init__(self, path_separator: str = os.path.sep, total_size: int = None, patcher: Any = None) -> None: """ Args: path_separator: optional substitute for os.path.sep total_size: if not None, the total size in bytes of the root filesystem. Example usage to use the same path separator under all systems: >>> filesystem = FakeFilesystem(path_separator='/') """ self.path_separator: str = path_separator self.alternative_path_separator: Optional[str] = os.path.altsep self.patcher = patcher if path_separator != os.sep: self.alternative_path_separator = None # is_windows_fs can be used to test the behavior of pyfakefs under # Windows fs on non-Windows systems and vice verse; # is it used to support drive letters, UNC paths and some other # Windows-specific features self.is_windows_fs = sys.platform == 'win32' # can be used to test some MacOS-specific behavior under other systems self.is_macos = sys.platform == 'darwin' # is_case_sensitive can be used to test pyfakefs for case-sensitive # file systems on non-case-sensitive systems and vice verse self.is_case_sensitive = not (self.is_windows_fs or self.is_macos) self.root = FakeDirectory(self.path_separator, filesystem=self) self.cwd = self.root.name # We can't query the current value without changing it: self.umask = os.umask(0o22) os.umask(self.umask) # A list of open file objects. Their position in the list is their # file descriptor number self.open_files: List[Optional[List[AnyFileWrapper]]] = [] # A heap containing all free positions in self.open_files list self._free_fd_heap: List[int] = [] # last used numbers for inodes (st_ino) and devices (st_dev) self.last_ino = 0 self.last_dev = 0 self.mount_points: Dict[AnyString, Dict] = {} self.add_mount_point(self.root.name, total_size) self._add_standard_streams() self.dev_null = FakeNullFile(self) # set from outside if needed self.patch_open_code = PatchMode.OFF self.shuffle_listdir_results = False @property def is_linux(self) -> bool: return not self.is_windows_fs and not self.is_macos @property def os(self) -> OSType: """Return the real or simulated type of operating system.""" return (OSType.WINDOWS if self.is_windows_fs else OSType.MACOS if self.is_macos else OSType.LINUX) @os.setter def os(self, value: OSType) -> None: """Set the simulated type of operating system underlying the fake file system.""" self.is_windows_fs = value == OSType.WINDOWS self.is_macos = value == OSType.MACOS self.is_case_sensitive = value == OSType.LINUX self.path_separator = '\\' if value == OSType.WINDOWS else '/' self.alternative_path_separator = ('/' if value == OSType.WINDOWS else None) self.reset() FakePathModule.reset(self) def reset(self, total_size: Optional[int] = None): """Remove all file system contents and reset the root.""" self.root = FakeDirectory(self.path_separator, filesystem=self) self.cwd = self.root.name self.open_files = [] self._free_fd_heap = [] self.last_ino = 0 self.last_dev = 0 self.mount_points = {} self.add_mount_point(self.root.name, total_size) self._add_standard_streams() from pyfakefs import fake_pathlib fake_pathlib.init_module(self) def pause(self) -> None: """Pause the patching of the file system modules until `resume` is called. After that call, all file system calls are executed in the real file system. Calling pause() twice is silently ignored. Only allowed if the file system object was created by a Patcher object. This is also the case for the pytest `fs` fixture. Raises: RuntimeError: if the file system was not created by a Patcher. """ if self.patcher is None: raise RuntimeError('pause() can only be called from a fake file ' 'system object created by a Patcher object') self.patcher.pause() def resume(self) -> None: """Resume the patching of the file system modules if `pause` has been called before. After that call, all file system calls are executed in the fake file system. Does nothing if patching is not paused. Raises: RuntimeError: if the file system has not been created by `Patcher`. """ if self.patcher is None: raise RuntimeError('resume() can only be called from a fake file ' 'system object created by a Patcher object') self.patcher.resume() def clear_cache(self) -> None: """Clear the cache of non-patched modules.""" if self.patcher: self.patcher.clear_cache() def line_separator(self) -> str: return '\r\n' if self.is_windows_fs else '\n' def _error_message(self, err_no: int) -> str: return os.strerror(err_no) + ' in the fake filesystem' def raise_os_error(self, err_no: int, filename: Optional[AnyString] = None, winerror: Optional[int] = None) -> NoReturn: """Raises OSError. The error message is constructed from the given error code and shall start with the error string issued in the real system. Note: this is not true under Windows if winerror is given - in this case a localized message specific to winerror will be shown in the real file system. Args: err_no: A numeric error code from the C variable errno. filename: The name of the affected file, if any. winerror: Windows only - the specific Windows error code. """ message = self._error_message(err_no) if (winerror is not None and sys.platform == 'win32' and self.is_windows_fs): raise OSError(err_no, message, filename, winerror) raise OSError(err_no, message, filename) def get_path_separator(self, path: AnyStr) -> AnyStr: """Return the path separator as the same type as path""" return matching_string(path, self.path_separator) def _alternative_path_separator( self, path: AnyStr) -> Optional[AnyStr]: """Return the alternative path separator as the same type as path""" return matching_string(path, self.alternative_path_separator) def _starts_with_sep(self, path: AnyStr) -> bool: """Return True if path starts with a path separator.""" sep = self.get_path_separator(path) altsep = self._alternative_path_separator(path) return (path.startswith(sep) or altsep is not None and path.startswith(altsep)) def add_mount_point(self, path: AnyStr, total_size: Optional[int] = None) -> Dict: """Add a new mount point for a filesystem device. The mount point gets a new unique device number. Args: path: The root path for the new mount path. total_size: The new total size of the added filesystem device in bytes. Defaults to infinite size. Returns: The newly created mount point dict. Raises: OSError: if trying to mount an existing mount point again. """ path = self.absnormpath(path) if path in self.mount_points: self.raise_os_error(errno.EEXIST, path) self.last_dev += 1 self.mount_points[path] = { 'idev': self.last_dev, 'total_size': total_size, 'used_size': 0 } # special handling for root path: has been created before if path == self.root.name: root_dir = self.root self.last_ino += 1 root_dir.st_ino = self.last_ino else: root_dir = self.create_dir(path) root_dir.st_dev = self.last_dev return self.mount_points[path] def _auto_mount_drive_if_needed(self, path: AnyStr, force: bool = False) -> Optional[Dict]: if (self.is_windows_fs and (force or not self._mount_point_for_path(path))): drive = self.splitdrive(path)[0] if drive: return self.add_mount_point(path=drive) return None def _mount_point_for_path(self, path: AnyStr) -> Dict: path = self.absnormpath(self._original_path(path)) for mount_path in self.mount_points: if path == matching_string(path, mount_path): return self.mount_points[mount_path] mount_path = matching_string(path, '') drive = self.splitdrive(path)[0] for root_path in self.mount_points: root_path = matching_string(path, root_path) if drive and not root_path.startswith(drive): continue if path.startswith(root_path) and len(root_path) > len(mount_path): mount_path = root_path if mount_path: return self.mount_points[to_string(mount_path)] mount_point = self._auto_mount_drive_if_needed(path, force=True) assert mount_point return mount_point def _mount_point_for_device(self, idev: int) -> Optional[Dict]: for mount_point in self.mount_points.values(): if mount_point['idev'] == idev: return mount_point return None def get_disk_usage( self, path: AnyStr = None) -> Tuple[int, int, int]: """Return the total, used and free disk space in bytes as named tuple, or placeholder values simulating unlimited space if not set. .. note:: This matches the return value of shutil.disk_usage(). Args: path: The disk space is returned for the file system device where `path` resides. Defaults to the root path (e.g. '/' on Unix systems). """ DiskUsage = namedtuple('DiskUsage', 'total, used, free') if path is None: mount_point = self.mount_points[self.root.name] else: mount_point = self._mount_point_for_path(path) if mount_point and mount_point['total_size'] is not None: return DiskUsage(mount_point['total_size'], mount_point['used_size'], mount_point['total_size'] - mount_point['used_size']) return DiskUsage( 1024 * 1024 * 1024 * 1024, 0, 1024 * 1024 * 1024 * 1024) def set_disk_usage( self, total_size: int, path: Optional[AnyStr] = None) -> None: """Changes the total size of the file system, preserving the used space. Example usage: set the size of an auto-mounted Windows drive. Args: total_size: The new total size of the filesystem in bytes. path: The disk space is changed for the file system device where `path` resides. Defaults to the root path (e.g. '/' on Unix systems). Raises: OSError: if the new space is smaller than the used size. """ file_path: AnyStr = (path if path is not None # type: ignore else self.root.name) mount_point = self._mount_point_for_path(file_path) if (mount_point['total_size'] is not None and mount_point['used_size'] > total_size): self.raise_os_error(errno.ENOSPC, path) mount_point['total_size'] = total_size def change_disk_usage(self, usage_change: int, file_path: AnyStr, st_dev: int) -> None: """Change the used disk space by the given amount. Args: usage_change: Number of bytes added to the used space. If negative, the used space will be decreased. file_path: The path of the object needing the disk space. st_dev: The device ID for the respective file system. Raises: OSError: if usage_change exceeds the free file system space """ mount_point = self._mount_point_for_device(st_dev) if mount_point: total_size = mount_point['total_size'] if total_size is not None: if total_size - mount_point['used_size'] < usage_change: self.raise_os_error(errno.ENOSPC, file_path) mount_point['used_size'] += usage_change def stat(self, entry_path: AnyStr, follow_symlinks: bool = True): """Return the os.stat-like tuple for the FakeFile object of entry_path. Args: entry_path: Path to filesystem object to retrieve. follow_symlinks: If False and entry_path points to a symlink, the link itself is inspected instead of the linked object. Returns: The FakeStatResult object corresponding to entry_path. Raises: OSError: if the filesystem object doesn't exist. """ # stat should return the tuple representing return value of os.stat try: file_object = self.resolve( entry_path, follow_symlinks, allow_fd=True, check_read_perm=False) except TypeError: file_object = self.resolve(entry_path) if not is_root(): # make sure stat raises if a parent dir is not readable parent_dir = file_object.parent_dir if parent_dir: self.get_object(parent_dir.path) # type: ignore[arg-type] self.raise_for_filepath_ending_with_separator( entry_path, file_object, follow_symlinks) return file_object.stat_result.copy() def raise_for_filepath_ending_with_separator( self, entry_path: AnyStr, file_object: FakeFile, follow_symlinks: bool = True, macos_handling: bool = False) -> None: if self.ends_with_path_separator(entry_path): if S_ISLNK(file_object.st_mode): try: link_object = self.resolve(entry_path) except OSError as exc: if self.is_macos and exc.errno != errno.ENOENT: return if self.is_windows_fs: self.raise_os_error(errno.EINVAL, entry_path) raise if not follow_symlinks or self.is_windows_fs or self.is_macos: file_object = link_object if self.is_windows_fs: is_error = S_ISREG(file_object.st_mode) elif self.is_macos and macos_handling: is_error = not S_ISLNK(file_object.st_mode) else: is_error = not S_ISDIR(file_object.st_mode) if is_error: error_nr = (errno.EINVAL if self.is_windows_fs else errno.ENOTDIR) self.raise_os_error(error_nr, entry_path) def chmod(self, path: AnyStr, mode: int, follow_symlinks: bool = True) -> None: """Change the permissions of a file as encoded in integer mode. Args: path: (str) Path to the file. mode: (int) Permissions. follow_symlinks: If `False` and `path` points to a symlink, the link itself is affected instead of the linked object. """ file_object = self.resolve(path, follow_symlinks, allow_fd=True) if self.is_windows_fs: if mode & PERM_WRITE: file_object.st_mode = file_object.st_mode | 0o222 else: file_object.st_mode = file_object.st_mode & 0o777555 else: file_object.st_mode = ((file_object.st_mode & ~PERM_ALL) | (mode & PERM_ALL)) file_object.st_ctime = now() def utime(self, path: AnyStr, times: Optional[Tuple[Union[int, float], Union[int, float]]] = None, *, ns: Optional[Tuple[int, int]] = None, follow_symlinks: bool = True) -> None: """Change the access and modified times of a file. Args: path: (str) Path to the file. times: 2-tuple of int or float numbers, of the form (atime, mtime) which is used to set the access and modified times in seconds. If None, both times are set to the current time. ns: 2-tuple of int numbers, of the form (atime, mtime) which is used to set the access and modified times in nanoseconds. If `None`, both times are set to the current time. follow_symlinks: If `False` and entry_path points to a symlink, the link itself is queried instead of the linked object. Raises: TypeError: If anything other than the expected types is specified in the passed `times` or `ns` tuple, or if the tuple length is not equal to 2. ValueError: If both times and ns are specified. """ self._handle_utime_arg_errors(ns, times) file_object = self.resolve(path, follow_symlinks, allow_fd=True) if times is not None: for file_time in times: if not isinstance(file_time, (int, float)): raise TypeError('atime and mtime must be numbers') file_object.st_atime = times[0] file_object.st_mtime = times[1] elif ns is not None: for file_time in ns: if not isinstance(file_time, int): raise TypeError('atime and mtime must be ints') file_object.st_atime_ns = ns[0] file_object.st_mtime_ns = ns[1] else: current_time = now() file_object.st_atime = current_time file_object.st_mtime = current_time def _handle_utime_arg_errors( self, ns: Optional[Tuple[int, int]], times: Optional[Tuple[Union[int, float], Union[int, float]]]): if times is not None and ns is not None: raise ValueError( "utime: you may specify either 'times' or 'ns' but not both") if times is not None and len(times) != 2: raise TypeError( "utime: 'times' must be either a tuple of two ints or None") if ns is not None and len(ns) != 2: raise TypeError("utime: 'ns' must be a tuple of two ints") @Deprecator def SetIno(self, path, st_ino): """Set the self.st_ino attribute of file at 'path'. Note that a unique inode is assigned automatically to a new fake file. Using this function does not guarantee uniqueness and should used with caution. Args: path: Path to file. st_ino: The desired inode. """ self.get_object(path).st_ino = st_ino def _add_open_file( self, file_obj: AnyFileWrapper) -> int: """Add file_obj to the list of open files on the filesystem. Used internally to manage open files. The position in the open_files array is the file descriptor number. Args: file_obj: File object to be added to open files list. Returns: File descriptor number for the file object. """ if self._free_fd_heap: open_fd = heapq.heappop(self._free_fd_heap) self.open_files[open_fd] = [file_obj] return open_fd self.open_files.append([file_obj]) return len(self.open_files) - 1 def _close_open_file(self, file_des: int) -> None: """Remove file object with given descriptor from the list of open files. Sets the entry in open_files to None. Args: file_des: Descriptor of file object to be removed from open files list. """ self.open_files[file_des] = None heapq.heappush(self._free_fd_heap, file_des) def get_open_file(self, file_des: int) -> AnyFileWrapper: """Return an open file. Args: file_des: File descriptor of the open file. Raises: OSError: an invalid file descriptor. TypeError: filedes is not an integer. Returns: Open file object. """ if not is_int_type(file_des): raise TypeError('an integer is required') valid = file_des < len(self.open_files) if valid: file_list = self.open_files[file_des] if file_list is not None: return file_list[0] self.raise_os_error(errno.EBADF, str(file_des)) def has_open_file(self, file_object: FakeFile) -> bool: """Return True if the given file object is in the list of open files. Args: file_object: The FakeFile object to be checked. Returns: `True` if the file is open. """ return (file_object in [wrappers[0].get_object() for wrappers in self.open_files if wrappers]) def _normalize_path_sep(self, path: AnyStr) -> AnyStr: alt_sep = self._alternative_path_separator(path) if alt_sep is not None: return path.replace(alt_sep, self.get_path_separator(path)) return path def normcase(self, path: AnyStr) -> AnyStr: """Replace all appearances of alternative path separator with path separator. Do nothing if no alternative separator is set. Args: path: The path to be normalized. Returns: The normalized path that will be used internally. """ file_path = make_string_path(path) return self._normalize_path_sep(file_path) def normpath(self, path: AnyStr) -> AnyStr: """Mimic os.path.normpath using the specified path_separator. Mimics os.path.normpath using the path_separator that was specified for this FakeFilesystem. Normalizes the path, but unlike the method absnormpath, does not make it absolute. Eliminates dot components (. and ..) and combines repeated path separators (//). Initial .. components are left in place for relative paths. If the result is an empty path, '.' is returned instead. This also replaces alternative path separator with path separator. That is, it behaves like the real os.path.normpath on Windows if initialized with '\\' as path separator and '/' as alternative separator. Args: path: (str) The path to normalize. Returns: (str) A copy of path with empty components and dot components removed. """ path_str = self.normcase(path) drive, path_str = self.splitdrive(path_str) sep = self.get_path_separator(path_str) is_absolute_path = path_str.startswith(sep) path_components: List[AnyStr] = path_str.split(sep) collapsed_path_components: List[AnyStr] = [] dot = matching_string(path_str, '.') dotdot = matching_string(path_str, '..') for component in path_components: if (not component) or (component == dot): continue if component == dotdot: if collapsed_path_components and ( collapsed_path_components[-1] != dotdot): # Remove an up-reference: directory/.. collapsed_path_components.pop() continue elif is_absolute_path: # Ignore leading .. components if starting from the # root directory. continue collapsed_path_components.append(component) collapsed_path = sep.join(collapsed_path_components) if is_absolute_path: collapsed_path = sep + collapsed_path return drive + collapsed_path or dot def _original_path(self, path: AnyStr) -> AnyStr: """Return a normalized case version of the given path for case-insensitive file systems. For case-sensitive file systems, return path unchanged. Args: path: the file path to be transformed Returns: A version of path matching the case of existing path elements. """ def components_to_path(): if len(path_components) > len(normalized_components): normalized_components.extend( to_string(p) for p in path_components[len( normalized_components):]) sep = self.path_separator normalized_path = sep.join(normalized_components) if (self._starts_with_sep(path) and not self._starts_with_sep(normalized_path)): normalized_path = sep + normalized_path return normalized_path if self.is_case_sensitive or not path: return path path_components = self._path_components(path) normalized_components = [] current_dir = self.root for component in path_components: if not isinstance(current_dir, FakeDirectory): return components_to_path() dir_name, directory = self._directory_content( current_dir, to_string(component)) if directory is None or ( isinstance(directory, FakeDirectory) and directory._byte_contents is None and directory.st_size == 0): return components_to_path() current_dir = cast(FakeDirectory, directory) normalized_components.append(dir_name) return components_to_path() def absnormpath(self, path: AnyStr) -> AnyStr: """Absolutize and minimalize the given path. Forces all relative paths to be absolute, and normalizes the path to eliminate dot and empty components. Args: path: Path to normalize. Returns: The normalized path relative to the current working directory, or the root directory if path is empty. """ path = self.normcase(path) cwd = matching_string(path, self.cwd) if not path: path = matching_string(path, self.path_separator) if path == matching_string(path, '.'): path = cwd elif not self._starts_with_root_path(path): # Prefix relative paths with cwd, if cwd is not root. root_name = matching_string(path, self.root.name) empty = matching_string(path, '') path = self.get_path_separator(path).join( (cwd != root_name and cwd or empty, path)) if path == matching_string(path, '.'): path = cwd return self.normpath(path) def splitpath(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]: """Mimic os.path.split using the specified path_separator. Mimics os.path.split using the path_separator that was specified for this FakeFilesystem. Args: path: (str) The path to split. Returns: (str) A duple (pathname, basename) for which pathname does not end with a slash, and basename does not contain a slash. """ path = make_string_path(path) sep = self.get_path_separator(path) alt_sep = self._alternative_path_separator(path) seps = sep if alt_sep is None else sep + alt_sep drive, path = self.splitdrive(path) i = len(path) while i and path[i-1] not in seps: i -= 1 head, tail = path[:i], path[i:] # now tail has no slashes # remove trailing slashes from head, unless it's all slashes head = head.rstrip(seps) or head return drive + head, tail def splitdrive(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]: """Splits the path into the drive part and the rest of the path. Taken from Windows specific implementation in Python 3.5 and slightly adapted. Args: path: the full path to be splitpath. Returns: A tuple of the drive part and the rest of the path, or of an empty string and the full path if drive letters are not supported or no drive is present. """ path_str = make_string_path(path) if self.is_windows_fs: if len(path_str) >= 2: norm_str = self.normcase(path_str) sep = self.get_path_separator(path_str) # UNC path_str handling if (norm_str[0:2] == sep * 2) and ( norm_str[2:3] != sep): # UNC path_str handling - splits off the mount point # instead of the drive sep_index = norm_str.find(sep, 2) if sep_index == -1: return path_str[:0], path_str sep_index2 = norm_str.find(sep, sep_index + 1) if sep_index2 == sep_index + 1: return path_str[:0], path_str if sep_index2 == -1: sep_index2 = len(path_str) return path_str[:sep_index2], path_str[sep_index2:] if path_str[1:2] == matching_string(path_str, ':'): return path_str[:2], path_str[2:] return path_str[:0], path_str def _join_paths_with_drive_support( self, *all_paths: AnyStr) -> AnyStr: """Taken from Python 3.5 os.path.join() code in ntpath.py and slightly adapted""" base_path = all_paths[0] paths_to_add = all_paths[1:] sep = self.get_path_separator(base_path) seps = [sep, self._alternative_path_separator(base_path)] result_drive, result_path = self.splitdrive(base_path) for path in paths_to_add: drive_part, path_part = self.splitdrive(path) if path_part and path_part[:1] in seps: # Second path is absolute if drive_part or not result_drive: result_drive = drive_part result_path = path_part continue elif drive_part and drive_part != result_drive: if (self.is_case_sensitive or drive_part.lower() != result_drive.lower()): # Different drives => ignore the first path entirely result_drive = drive_part result_path = path_part continue # Same drive in different case result_drive = drive_part # Second path is relative to the first if result_path and result_path[-1:] not in seps: result_path = result_path + sep result_path = result_path + path_part # add separator between UNC and non-absolute path colon = matching_string(base_path, ':') if (result_path and result_path[:1] not in seps and result_drive and result_drive[-1:] != colon): return result_drive + sep + result_path return result_drive + result_path def joinpaths(self, *paths: AnyStr) -> AnyStr: """Mimic os.path.join using the specified path_separator. Args: *paths: (str) Zero or more paths to join. Returns: (str) The paths joined by the path separator, starting with the last absolute path in paths. """ file_paths = [os.fspath(path) for path in paths] if len(file_paths) == 1: return paths[0] if self.is_windows_fs: return self._join_paths_with_drive_support(*file_paths) joined_path_segments = [] sep = self.get_path_separator(file_paths[0]) for path_segment in file_paths: if self._starts_with_root_path(path_segment): # An absolute path joined_path_segments = [path_segment] else: if (joined_path_segments and not joined_path_segments[-1].endswith(sep)): joined_path_segments.append(sep) if path_segment: joined_path_segments.append(path_segment) return matching_string(file_paths[0], '').join(joined_path_segments) @overload def _path_components(self, path: str) -> List[str]: ... @overload def _path_components(self, path: bytes) -> List[bytes]: ... def _path_components(self, path: AnyStr) -> List[AnyStr]: """Breaks the path into a list of component names. Does not include the root directory as a component, as all paths are considered relative to the root directory for the FakeFilesystem. Callers should basically follow this pattern: .. code:: python file_path = self.absnormpath(file_path) path_components = self._path_components(file_path) current_dir = self.root for component in path_components: if component not in current_dir.entries: raise OSError _do_stuff_with_component(current_dir, component) current_dir = current_dir.get_entry(component) Args: path: Path to tokenize. Returns: The list of names split from path. """ if not path or path == self.get_path_separator(path): return [] drive, path = self.splitdrive(path) path_components = path.split(self.get_path_separator(path)) assert drive or path_components if not path_components[0]: if len(path_components) > 1 and not path_components[1]: path_components = [] else: # This is an absolute path. path_components = path_components[1:] if drive: path_components.insert(0, drive) return path_components def _starts_with_drive_letter(self, file_path: AnyStr) -> bool: """Return True if file_path starts with a drive letter. Args: file_path: the full path to be examined. Returns: `True` if drive letter support is enabled in the filesystem and the path starts with a drive letter. """ colon = matching_string(file_path, ':') if (len(file_path) >= 2 and file_path[:1].isalpha and file_path[1:2] == colon): if self.is_windows_fs: return True if os.name == 'nt': # special case if we are emulating Posix under Windows # check if the path exists because it has been mapped in # this is not foolproof, but handles most cases try: self.get_object_from_normpath(file_path) return True except OSError: return False return False def _starts_with_root_path(self, file_path: AnyStr) -> bool: root_name = matching_string(file_path, self.root.name) file_path = self._normalize_path_sep(file_path) return (file_path.startswith(root_name) or not self.is_case_sensitive and file_path.lower().startswith( root_name.lower()) or self._starts_with_drive_letter(file_path)) def _is_root_path(self, file_path: AnyStr) -> bool: root_name = matching_string(file_path, self.root.name) return (file_path == root_name or not self.is_case_sensitive and file_path.lower() == root_name.lower() or 2 <= len(file_path) <= 3 and self._starts_with_drive_letter(file_path)) def ends_with_path_separator(self, path: Union[int, AnyPath]) -> bool: """Return True if ``file_path`` ends with a valid path separator.""" if isinstance(path, int): return False file_path = make_string_path(path) if not file_path: return False sep = self.get_path_separator(file_path) altsep = self._alternative_path_separator(file_path) return (file_path not in (sep, altsep) and (file_path.endswith(sep) or altsep is not None and file_path.endswith(altsep))) def is_filepath_ending_with_separator(self, path: AnyStr) -> bool: if not self.ends_with_path_separator(path): return False return self.isfile(self._path_without_trailing_separators(path)) def _directory_content(self, directory: FakeDirectory, component: str) -> Tuple[Optional[str], Optional[AnyFile]]: if not isinstance(directory, FakeDirectory): return None, None if component in directory.entries: return component, directory.entries[component] if not self.is_case_sensitive: matching_content = [(subdir, directory.entries[subdir]) for subdir in directory.entries if subdir.lower() == component.lower()] if matching_content: return matching_content[0] return None, None def exists(self, file_path: AnyPath, check_link: bool = False) -> bool: """Return true if a path points to an existing file system object. Args: file_path: The path to examine. check_link: If True, links are not followed Returns: (bool) True if the corresponding object exists. Raises: TypeError: if file_path is None. """ if check_link and self.islink(file_path): return True path = to_string(make_string_path(file_path)) if path is None: raise TypeError if not path: return False if path == self.dev_null.name: return not self.is_windows_fs or sys.version_info >= (3, 8) try: if self.is_filepath_ending_with_separator(path): return False path = self.resolve_path(path) except OSError: return False if path == self.root.name: return True path_components: List[str] = self._path_components(path) current_dir = self.root for component in path_components: directory = self._directory_content( current_dir, to_string(component))[1] if directory is None: return False current_dir = cast(FakeDirectory, directory) return True def resolve_path(self, file_path: AnyStr, allow_fd: bool = False) -> AnyStr: """Follow a path, resolving symlinks. ResolvePath traverses the filesystem along the specified file path, resolving file names and symbolic links until all elements of the path are exhausted, or we reach a file which does not exist. If all the elements are not consumed, they just get appended to the path resolved so far. This gives us the path which is as resolved as it can be, even if the file does not exist. This behavior mimics Unix semantics, and is best shown by example. Given a file system that looks like this: /a/b/ /a/b/c -> /a/b2 c is a symlink to /a/b2 /a/b2/x /a/c -> ../d /a/x -> y Then: /a/b/x => /a/b/x /a/c => /a/d /a/x => /a/y /a/b/c/d/e => /a/b2/d/e Args: file_path: The path to examine. allow_fd: If `True`, `file_path` may be open file descriptor. Returns: The resolved_path (str or byte). Raises: TypeError: if `file_path` is `None`. OSError: if `file_path` is '' or a part of the path doesn't exist. """ if allow_fd and isinstance(file_path, int): return self.get_open_file(file_path).get_object().path path = make_string_path(file_path) if path is None: # file.open(None) raises TypeError, so mimic that. raise TypeError('Expected file system path string, received None') if not path or not self._valid_relative_path(path): # file.open('') raises OSError, so mimic that, and validate that # all parts of a relative path exist. self.raise_os_error(errno.ENOENT, path) path = self.absnormpath(self._original_path(path)) if self._is_root_path(path): return path if path == matching_string(path, self.dev_null.name): return path path_components = self._path_components(path) resolved_components = self._resolve_components(path_components) return self._components_to_path(resolved_components) def _components_to_path(self, component_folders): sep = (self.get_path_separator(component_folders[0]) if component_folders else self.path_separator) path = sep.join(component_folders) if not self._starts_with_root_path(path): path = sep + path return path def _resolve_components(self, components: List[AnyStr]) -> List[str]: current_dir = self.root link_depth = 0 path_components = [to_string(comp) for comp in components] resolved_components: List[str] = [] while path_components: component = path_components.pop(0) resolved_components.append(component) directory = self._directory_content(current_dir, component)[1] if directory is None: # The component of the path at this point does not actually # exist in the folder. We can't resolve the path any more. # It is legal to link to a file that does not yet exist, so # rather than raise an error, we just append the remaining # components to what return path we have built so far and # return that. resolved_components.extend(path_components) break # Resolve any possible symlinks in the current path component. elif S_ISLNK(directory.st_mode): # This link_depth check is not really meant to be an accurate # check. It is just a quick hack to prevent us from looping # forever on cycles. if link_depth > _MAX_LINK_DEPTH: self.raise_os_error(errno.ELOOP, self._components_to_path( resolved_components)) link_path = self._follow_link(resolved_components, directory) # Following the link might result in the complete replacement # of the current_dir, so we evaluate the entire resulting path. target_components = self._path_components(link_path) path_components = target_components + path_components resolved_components = [] current_dir = self.root link_depth += 1 else: current_dir = cast(FakeDirectory, directory) return resolved_components def _valid_relative_path(self, file_path: AnyStr) -> bool: if self.is_windows_fs: return True slash_dotdot = matching_string( file_path, self.path_separator + '..') while file_path and slash_dotdot in file_path: file_path = file_path[:file_path.rfind(slash_dotdot)] if not self.exists(self.absnormpath(file_path)): return False return True def _follow_link(self, link_path_components: List[str], link: AnyFile) -> str: """Follow a link w.r.t. a path resolved so far. The component is either a real file, which is a no-op, or a symlink. In the case of a symlink, we have to modify the path as built up so far /a/b => ../c should yield /a/../c (which will normalize to /a/c) /a/b => x should yield /a/x /a/b => /x/y/z should yield /x/y/z The modified path may land us in a new spot which is itself a link, so we may repeat the process. Args: link_path_components: The resolved path built up to the link so far. link: The link object itself. Returns: (string) The updated path resolved after following the link. Raises: OSError: if there are too many levels of symbolic link """ link_path = link.contents if link_path is not None: # ignore UNC prefix for local files if self.is_windows_fs and link_path.startswith('\\\\?\\'): link_path = link_path[4:] sep = self.get_path_separator(link_path) # For links to absolute paths, we want to throw out everything # in the path built so far and replace with the link. For relative # links, we have to append the link to what we have so far, if not self._starts_with_root_path(link_path): # Relative path. Append remainder of path to what we have # processed so far, excluding the name of the link itself. # /a/b => ../c should yield /a/../c # (which will normalize to /c) # /a/b => d should yield a/d components = link_path_components[:-1] components.append(link_path) link_path = sep.join(components) # Don't call self.NormalizePath(), as we don't want to prepend # self.cwd. return self.normpath(link_path) raise ValueError("Invalid link") def get_object_from_normpath(self, file_path: AnyPath, check_read_perm: bool = True) -> AnyFile: """Search for the specified filesystem object within the fake filesystem. Args: file_path: Specifies target FakeFile object to retrieve, with a path that has already been normalized/resolved. check_read_perm: If True, raises OSError if a parent directory does not have read permission Returns: The FakeFile object corresponding to file_path. Raises: OSError: if the object is not found. """ path = make_string_path(file_path) if path == matching_string(path, self.root.name): return self.root if path == matching_string(path, self.dev_null.name): return self.dev_null path = self._original_path(path) path_components = self._path_components(path) target = self.root try: for component in path_components: if S_ISLNK(target.st_mode): if target.contents: target = cast(FakeDirectory, self.resolve(target.contents)) if not S_ISDIR(target.st_mode): if not self.is_windows_fs: self.raise_os_error(errno.ENOTDIR, path) self.raise_os_error(errno.ENOENT, path) target = target.get_entry(component) # type: ignore if (not is_root() and check_read_perm and target and not target.st_mode & PERM_READ): self.raise_os_error(errno.EACCES, target.path) except KeyError: self.raise_os_error(errno.ENOENT, path) return target def get_object(self, file_path: AnyPath, check_read_perm: bool = True) -> FakeFile: """Search for the specified filesystem object within the fake filesystem. Args: file_path: Specifies the target FakeFile object to retrieve. check_read_perm: If True, raises OSError if a parent directory does not have read permission Returns: The FakeFile object corresponding to `file_path`. Raises: OSError: if the object is not found. """ path = make_string_path(file_path) path = self.absnormpath(self._original_path(path)) return self.get_object_from_normpath(path, check_read_perm) def resolve(self, file_path: AnyStr, follow_symlinks: bool = True, allow_fd: bool = False, check_read_perm: bool = True) -> FakeFile: """Search for the specified filesystem object, resolving all links. Args: file_path: Specifies the target FakeFile object to retrieve. follow_symlinks: If `False`, the link itself is resolved, otherwise the object linked to. allow_fd: If `True`, `file_path` may be an open file descriptor check_read_perm: If True, raises OSError if a parent directory does not have read permission Returns: The FakeFile object corresponding to `file_path`. Raises: OSError: if the object is not found. """ if isinstance(file_path, int): if allow_fd: return self.get_open_file(file_path).get_object() raise TypeError('path should be string, bytes or ' 'os.PathLike, not int') if follow_symlinks: return self.get_object_from_normpath(self.resolve_path( file_path, check_read_perm), check_read_perm) return self.lresolve(file_path) def lresolve(self, path: AnyPath) -> FakeFile: """Search for the specified object, resolving only parent links. This is analogous to the stat/lstat difference. This resolves links *to* the object but not of the final object itself. Args: path: Specifies target FakeFile object to retrieve. Returns: The FakeFile object corresponding to path. Raises: OSError: if the object is not found. """ path_str = make_string_path(path) if not path_str: raise OSError(errno.ENOENT, path_str) if path_str == matching_string(path_str, self.root.name): # The root directory will never be a link return self.root # remove trailing separator path_str = self._path_without_trailing_separators(path_str) if path_str == matching_string(path_str, '.'): path_str = matching_string(path_str, self.cwd) path_str = self._original_path(path_str) parent_directory, child_name = self.splitpath(path_str) if not parent_directory: parent_directory = matching_string(path_str, self.cwd) try: parent_obj = self.resolve(parent_directory) assert parent_obj if not isinstance(parent_obj, FakeDirectory): if not self.is_windows_fs and isinstance(parent_obj, FakeFile): self.raise_os_error(errno.ENOTDIR, path_str) self.raise_os_error(errno.ENOENT, path_str) if not parent_obj.st_mode & PERM_READ: self.raise_os_error(errno.EACCES, parent_directory) return (parent_obj.get_entry(to_string(child_name)) if child_name else parent_obj) except KeyError: self.raise_os_error(errno.ENOENT, path_str) def add_object(self, file_path: AnyStr, file_object: AnyFile) -> None: """Add a fake file or directory into the filesystem at file_path. Args: file_path: The path to the file to be added relative to self. file_object: File or directory to add. Raises: OSError: if file_path does not correspond to a directory. """ if not file_path: target_directory = self.root else: target_directory = cast(FakeDirectory, self.resolve(file_path)) if not S_ISDIR(target_directory.st_mode): error = errno.ENOENT if self.is_windows_fs else errno.ENOTDIR self.raise_os_error(error, file_path) target_directory.add_entry(file_object) def rename(self, old_file_path: AnyPath, new_file_path: AnyPath, force_replace: bool = False) -> None: """Renames a FakeFile object at old_file_path to new_file_path, preserving all properties. Args: old_file_path: Path to filesystem object to rename. new_file_path: Path to where the filesystem object will live after this call. force_replace: If set and destination is an existing file, it will be replaced even under Windows if the user has permissions, otherwise replacement happens under Unix only. Raises: OSError: if old_file_path does not exist. OSError: if new_file_path is an existing directory (Windows, or Posix if old_file_path points to a regular file) OSError: if old_file_path is a directory and new_file_path a file OSError: if new_file_path is an existing file and force_replace not set (Windows only). OSError: if new_file_path is an existing file and could not be removed (Posix, or Windows with force_replace set). OSError: if dirname(new_file_path) does not exist. OSError: if the file would be moved to another filesystem (e.g. mount point). """ old_path = make_string_path(old_file_path) new_path = make_string_path(new_file_path) ends_with_sep = self.ends_with_path_separator(old_path) old_path = self.absnormpath(old_path) new_path = self.absnormpath(new_path) if not self.exists(old_path, check_link=True): self.raise_os_error(errno.ENOENT, old_path, 2) if ends_with_sep: self._handle_broken_link_with_trailing_sep(old_path) old_object = self.lresolve(old_path) if not self.is_windows_fs: self._handle_posix_dir_link_errors( new_path, old_path, ends_with_sep) if self.exists(new_path, check_link=True): renamed_path = self._rename_to_existing_path( force_replace, new_path, old_path, old_object, ends_with_sep) if renamed_path is None: return else: new_path = renamed_path old_dir, old_name = self.splitpath(old_path) new_dir, new_name = self.splitpath(new_path) if not self.exists(new_dir): self.raise_os_error(errno.ENOENT, new_dir) old_dir_object = self.resolve(old_dir) new_dir_object = self.resolve(new_dir) if old_dir_object.st_dev != new_dir_object.st_dev: self.raise_os_error(errno.EXDEV, old_path) if not S_ISDIR(new_dir_object.st_mode): self.raise_os_error( errno.EACCES if self.is_windows_fs else errno.ENOTDIR, new_path) if new_dir_object.has_parent_object(old_object): self.raise_os_error(errno.EINVAL, new_path) self._do_rename(old_dir_object, old_name, new_dir_object, new_name) def _do_rename(self, old_dir_object, old_name, new_dir_object, new_name): object_to_rename = old_dir_object.get_entry(old_name) old_dir_object.remove_entry(old_name, recursive=False) object_to_rename.name = new_name new_name = new_dir_object._normalized_entryname(new_name) old_entry = (new_dir_object.get_entry(new_name) if new_name in new_dir_object.entries else None) try: if old_entry: # in case of overwriting remove the old entry first new_dir_object.remove_entry(new_name) new_dir_object.add_entry(object_to_rename) except OSError: # adding failed, roll back the changes before re-raising if old_entry and new_name not in new_dir_object.entries: new_dir_object.add_entry(old_entry) object_to_rename.name = old_name old_dir_object.add_entry(object_to_rename) raise def _handle_broken_link_with_trailing_sep(self, path: AnyStr) -> None: # note that the check for trailing sep has to be done earlier if self.islink(path): if not self.exists(path): error = (errno.ENOENT if self.is_macos else errno.EINVAL if self.is_windows_fs else errno.ENOTDIR) self.raise_os_error(error, path) def _handle_posix_dir_link_errors(self, new_file_path: AnyStr, old_file_path: AnyStr, ends_with_sep: bool) -> None: if (self.isdir(old_file_path, follow_symlinks=False) and self.islink(new_file_path)): self.raise_os_error(errno.ENOTDIR, new_file_path) if (self.isdir(new_file_path, follow_symlinks=False) and self.islink(old_file_path)): if ends_with_sep and self.is_macos: return error = errno.ENOTDIR if ends_with_sep else errno.EISDIR self.raise_os_error(error, new_file_path) if (ends_with_sep and self.islink(old_file_path) and old_file_path == new_file_path and not self.is_windows_fs): self.raise_os_error(errno.ENOTDIR, new_file_path) def _rename_to_existing_path(self, force_replace: bool, new_file_path: AnyStr, old_file_path: AnyStr, old_object: FakeFile, ends_with_sep: bool) -> Optional[AnyStr]: new_object = self.get_object(new_file_path) if old_file_path == new_file_path: if not S_ISLNK(new_object.st_mode) and ends_with_sep: error = errno.EINVAL if self.is_windows_fs else errno.ENOTDIR self.raise_os_error(error, old_file_path) return None # Nothing to do here if old_object == new_object: return self._rename_same_object(new_file_path, old_file_path) if S_ISDIR(new_object.st_mode) or S_ISLNK(new_object.st_mode): self._handle_rename_error_for_dir_or_link( force_replace, new_file_path, new_object, old_object, ends_with_sep) elif S_ISDIR(old_object.st_mode): error = errno.EEXIST if self.is_windows_fs else errno.ENOTDIR self.raise_os_error(error, new_file_path) elif self.is_windows_fs and not force_replace: self.raise_os_error(errno.EEXIST, new_file_path) else: self.remove_object(new_file_path) return new_file_path def _handle_rename_error_for_dir_or_link(self, force_replace: bool, new_file_path: AnyStr, new_object: FakeFile, old_object: FakeFile, ends_with_sep: bool) -> None: if self.is_windows_fs: if force_replace: self.raise_os_error(errno.EACCES, new_file_path) else: self.raise_os_error(errno.EEXIST, new_file_path) if not S_ISLNK(new_object.st_mode): if new_object.entries: if (not S_ISLNK(old_object.st_mode) or not ends_with_sep or not self.is_macos): self.raise_os_error(errno.ENOTEMPTY, new_file_path) if S_ISREG(old_object.st_mode): self.raise_os_error(errno.EISDIR, new_file_path) def _rename_same_object(self, new_file_path: AnyStr, old_file_path: AnyStr) -> Optional[AnyStr]: do_rename = old_file_path.lower() == new_file_path.lower() if not do_rename: try: real_old_path = self.resolve_path(old_file_path) original_old_path = self._original_path(real_old_path) real_new_path = self.resolve_path(new_file_path) if (real_new_path == original_old_path and (new_file_path == real_old_path) == (new_file_path.lower() == real_old_path.lower())): real_object = self.resolve(old_file_path, follow_symlinks=False) do_rename = (os.path.basename(old_file_path) == real_object.name or not self.is_macos) else: do_rename = (real_new_path.lower() == real_old_path.lower()) if do_rename: # only case is changed in case-insensitive file # system - do the rename parent, file_name = self.splitpath(new_file_path) new_file_path = self.joinpaths( self._original_path(parent), file_name) except OSError: # ResolvePath may fail due to symlink loop issues or # similar - in this case just assume the paths # to be different pass if not do_rename: # hard links to the same file - nothing to do return None return new_file_path def remove_object(self, file_path: AnyStr) -> None: """Remove an existing file or directory. Args: file_path: The path to the file relative to self. Raises: OSError: if file_path does not correspond to an existing file, or if part of the path refers to something other than a directory. OSError: if the directory is in use (eg, if it is '/'). """ file_path = self.absnormpath(self._original_path(file_path)) if self._is_root_path(file_path): self.raise_os_error(errno.EBUSY, file_path) try: dirname, basename = self.splitpath(file_path) target_directory = self.resolve(dirname, check_read_perm=False) target_directory.remove_entry(basename) except KeyError: self.raise_os_error(errno.ENOENT, file_path) except AttributeError: self.raise_os_error(errno.ENOTDIR, file_path) def make_string_path(self, path: AnyPath) -> AnyStr: path_str = make_string_path(path) os_sep = matching_string(path_str, os.sep) fake_sep = matching_string(path_str, self.path_separator) return path_str.replace(os_sep, fake_sep) # type: ignore[return-value] def create_dir(self, directory_path: AnyPath, perm_bits: int = PERM_DEF) -> FakeDirectory: """Create `directory_path`, and all the parent directories. Helper method to set up your test faster. Args: directory_path: The full directory path to create. perm_bits: The permission bits as set by `chmod`. Returns: The newly created FakeDirectory object. Raises: OSError: if the directory already exists. """ dir_path = self.make_string_path(directory_path) dir_path = self.absnormpath(dir_path) self._auto_mount_drive_if_needed(dir_path) if (self.exists(dir_path, check_link=True) and dir_path not in self.mount_points): self.raise_os_error(errno.EEXIST, dir_path) path_components = self._path_components(dir_path) current_dir = self.root new_dirs = [] for component in [to_string(p) for p in path_components]: directory = self._directory_content( current_dir, to_string(component))[1] if not directory: new_dir = FakeDirectory(component, filesystem=self) new_dirs.append(new_dir) current_dir.add_entry(new_dir) current_dir = new_dir else: if S_ISLNK(directory.st_mode): assert directory.contents directory = self.resolve(directory.contents) assert directory current_dir = cast(FakeDirectory, directory) if directory.st_mode & S_IFDIR != S_IFDIR: self.raise_os_error(errno.ENOTDIR, current_dir.path) # set the permission after creating the directories # to allow directory creation inside a read-only directory for new_dir in new_dirs: new_dir.st_mode = S_IFDIR | perm_bits return current_dir def create_file(self, file_path: AnyPath, st_mode: int = S_IFREG | PERM_DEF_FILE, contents: AnyString = '', st_size: Optional[int] = None, create_missing_dirs: bool = True, apply_umask: bool = False, encoding: Optional[str] = None, errors: Optional[str] = None, side_effect: Optional[Callable] = None) -> FakeFile: """Create `file_path`, including all the parent directories along the way. This helper method can be used to set up tests more easily. Args: file_path: The path to the file to create. st_mode: The stat constant representing the file type. contents: the contents of the file. If not given and st_size is None, an empty file is assumed. st_size: file size; only valid if contents not given. If given, the file is considered to be in "large file mode" and trying to read from or write to the file will result in an exception. create_missing_dirs: If `True`, auto create missing directories. apply_umask: `True` if the current umask must be applied on `st_mode`. encoding: If `contents` is a unicode string, the encoding used for serialization. errors: The error mode used for encoding/decoding errors. side_effect: function handle that is executed when file is written, must accept the file object as an argument. Returns: The newly created FakeFile object. Raises: OSError: if the file already exists. OSError: if the containing directory is required and missing. """ return self.create_file_internally( file_path, st_mode, contents, st_size, create_missing_dirs, apply_umask, encoding, errors, side_effect=side_effect) def add_real_file(self, source_path: AnyPath, read_only: bool = True, target_path: Optional[AnyPath] = None) -> FakeFile: """Create `file_path`, including all the parent directories along the way, for an existing real file. The contents of the real file are read only on demand. Args: source_path: Path to an existing file in the real file system read_only: If `True` (the default), writing to the fake file raises an exception. Otherwise, writing to the file changes the fake file only. target_path: If given, the path of the target direction, otherwise it is equal to `source_path`. Returns: the newly created FakeFile object. Raises: OSError: if the file does not exist in the real file system. OSError: if the file already exists in the fake file system. .. note:: On most systems, accessing the fake file's contents may update both the real and fake files' `atime` (access time). In this particular case, `add_real_file()` violates the rule that `pyfakefs` must not modify the real file system. """ target_path = target_path or source_path source_path_str = make_string_path(source_path) real_stat = os.stat(source_path_str) fake_file = self.create_file_internally(target_path, read_from_real_fs=True) # for read-only mode, remove the write/executable permission bits fake_file.stat_result.set_from_stat_result(real_stat) if read_only: fake_file.st_mode &= 0o777444 fake_file.file_path = source_path_str self.change_disk_usage(fake_file.size, fake_file.name, fake_file.st_dev) return fake_file def add_real_symlink(self, source_path: AnyPath, target_path: Optional[AnyPath] = None) -> FakeFile: """Create a symlink at source_path (or target_path, if given). It will point to the same path as the symlink on the real filesystem. Relative symlinks will point relative to their new location. Absolute symlinks will point to the same, absolute path as on the real filesystem. Args: source_path: The path to the existing symlink. target_path: If given, the name of the symlink in the fake filesystem, otherwise, the same as `source_path`. Returns: the newly created FakeFile object. Raises: OSError: if the directory does not exist in the real file system. OSError: if the symlink could not be created (see :py:meth:`create_file`). OSError: if the directory already exists in the fake file system. """ source_path_str = make_string_path(source_path) # TODO: add test source_path_str = self._path_without_trailing_separators( source_path_str) if (not os.path.exists(source_path_str) and not os.path.islink(source_path_str)): self.raise_os_error(errno.ENOENT, source_path_str) target = os.readlink(source_path_str) if target_path: return self.create_symlink(target_path, target) else: return self.create_symlink(source_path_str, target) def add_real_directory( self, source_path: AnyPath, read_only: bool = True, lazy_read: bool = True, target_path: Optional[AnyPath] = None) -> FakeDirectory: """Create a fake directory corresponding to the real directory at the specified path. Add entries in the fake directory corresponding to the entries in the real directory. Symlinks are supported. Args: source_path: The path to the existing directory. read_only: If set, all files under the directory are treated as read-only, e.g. a write access raises an exception; otherwise, writing to the files changes the fake files only as usually. lazy_read: If set (default), directory contents are only read when accessed, and only until the needed subdirectory level. .. note:: This means that the file system size is only updated at the time the directory contents are read; set this to `False` only if you are dependent on accurate file system size in your test target_path: If given, the target directory, otherwise, the target directory is the same as `source_path`. Returns: the newly created FakeDirectory object. Raises: OSError: if the directory does not exist in the real file system. OSError: if the directory already exists in the fake file system. """ source_path_str = make_string_path(source_path) # TODO: add test source_path_str = self._path_without_trailing_separators( source_path_str) if not os.path.exists(source_path_str): self.raise_os_error(errno.ENOENT, source_path_str) target_path = target_path or source_path_str new_dir: FakeDirectory if lazy_read: parent_path = os.path.split(target_path)[0] if self.exists(parent_path): parent_dir = self.get_object(parent_path) else: parent_dir = self.create_dir(parent_path) new_dir = FakeDirectoryFromRealDirectory( source_path_str, self, read_only, target_path) parent_dir.add_entry(new_dir) else: new_dir = self.create_dir(target_path) for base, _, files in os.walk(source_path_str): new_base = os.path.join(new_dir.path, # type: ignore[arg-type] os.path.relpath(base, source_path_str)) for fileEntry in os.listdir(base): abs_fileEntry = os.path.join(base, fileEntry) if not os.path.islink(abs_fileEntry): continue self.add_real_symlink( abs_fileEntry, os.path.join(new_base, fileEntry)) for fileEntry in files: path = os.path.join(base, fileEntry) if os.path.islink(path): continue self.add_real_file(path, read_only, os.path.join(new_base, fileEntry)) return new_dir def add_real_paths(self, path_list: List[AnyStr], read_only: bool = True, lazy_dir_read: bool = True) -> None: """This convenience method adds multiple files and/or directories from the real file system to the fake file system. See `add_real_file()` and `add_real_directory()`. Args: path_list: List of file and directory paths in the real file system. read_only: If set, all files and files under under the directories are treated as read-only, e.g. a write access raises an exception; otherwise, writing to the files changes the fake files only as usually. lazy_dir_read: Uses lazy reading of directory contents if set (see `add_real_directory`) Raises: OSError: if any of the files and directories in the list does not exist in the real file system. OSError: if any of the files and directories in the list already exists in the fake file system. """ for path in path_list: if os.path.isdir(path): self.add_real_directory(path, read_only, lazy_dir_read) else: self.add_real_file(path, read_only) def create_file_internally( self, file_path: AnyPath, st_mode: int = S_IFREG | PERM_DEF_FILE, contents: AnyString = '', st_size: Optional[int] = None, create_missing_dirs: bool = True, apply_umask: bool = False, encoding: Optional[str] = None, errors: Optional[str] = None, read_from_real_fs: bool = False, side_effect: Optional[Callable] = None) -> FakeFile: """Internal fake file creator that supports both normal fake files and fake files based on real files. Args: file_path: path to the file to create. st_mode: the stat.S_IF constant representing the file type. contents: the contents of the file. If not given and st_size is None, an empty file is assumed. st_size: file size; only valid if contents not given. If given, the file is considered to be in "large file mode" and trying to read from or write to the file will result in an exception. create_missing_dirs: if True, auto create missing directories. apply_umask: whether or not the current umask must be applied on st_mode. encoding: if contents is a unicode string, the encoding used for serialization. errors: the error mode used for encoding/decoding errors read_from_real_fs: if True, the contents are read from the real file system on demand. side_effect: function handle that is executed when file is written, must accept the file object as an argument. """ path = self.make_string_path(file_path) path = self.absnormpath(path) if not is_int_type(st_mode): raise TypeError( 'st_mode must be of int type - did you mean to set contents?') if self.exists(path, check_link=True): self.raise_os_error(errno.EEXIST, path) parent_directory, new_file = self.splitpath(path) if not parent_directory: parent_directory = matching_string(path, self.cwd) self._auto_mount_drive_if_needed(parent_directory) if not self.exists(parent_directory): if not create_missing_dirs: self.raise_os_error(errno.ENOENT, parent_directory) self.create_dir(parent_directory) else: parent_directory = self._original_path(parent_directory) if apply_umask: st_mode &= ~self.umask file_object: FakeFile if read_from_real_fs: file_object = FakeFileFromRealFile(to_string(path), filesystem=self, side_effect=side_effect) else: file_object = FakeFile(new_file, st_mode, filesystem=self, encoding=encoding, errors=errors, side_effect=side_effect) self.add_object(parent_directory, file_object) if st_size is None and contents is None: contents = '' if (not read_from_real_fs and (contents is not None or st_size is not None)): try: if st_size is not None: file_object.set_large_file_size(st_size) else: file_object.set_initial_contents(contents) # type: ignore except OSError: self.remove_object(path) raise return file_object def create_symlink(self, file_path: AnyPath, link_target: AnyPath, create_missing_dirs: bool = True) -> FakeFile: """Create the specified symlink, pointed at the specified link target. Args: file_path: path to the symlink to create link_target: the target of the symlink create_missing_dirs: If `True`, any missing parent directories of file_path will be created Returns: The newly created FakeFile object. Raises: OSError: if the symlink could not be created (see :py:meth:`create_file`). """ link_path = self.make_string_path(file_path) link_target_path = self.make_string_path(link_target) link_path = self.normcase(link_path) # the link path cannot end with a path separator if self.ends_with_path_separator(link_path): if self.exists(link_path): self.raise_os_error(errno.EEXIST, link_path) if self.exists(link_target_path): if not self.is_windows_fs: self.raise_os_error(errno.ENOENT, link_path) else: if self.is_windows_fs: self.raise_os_error(errno.EINVAL, link_target_path) if not self.exists( self._path_without_trailing_separators(link_path), check_link=True): self.raise_os_error(errno.ENOENT, link_target_path) if self.is_macos: # to avoid EEXIST exception, remove the link # if it already exists if self.exists(link_path, check_link=True): self.remove_object(link_path) else: self.raise_os_error(errno.EEXIST, link_target_path) # resolve the link path only if it is not a link itself if not self.islink(link_path): link_path = self.resolve_path(link_path) return self.create_file_internally( link_path, st_mode=S_IFLNK | PERM_DEF, contents=link_target_path, create_missing_dirs=create_missing_dirs) def create_link(self, old_path: AnyPath, new_path: AnyPath, follow_symlinks: bool = True, create_missing_dirs: bool = True) -> FakeFile: """Create a hard link at new_path, pointing at old_path. Args: old_path: An existing link to the target file. new_path: The destination path to create a new link at. follow_symlinks: If False and old_path is a symlink, link the symlink instead of the object it points to. create_missing_dirs: If `True`, any missing parent directories of file_path will be created Returns: The FakeFile object referred to by old_path. Raises: OSError: if something already exists at new_path. OSError: if old_path is a directory. OSError: if the parent directory doesn't exist. """ old_path_str = make_string_path(old_path) new_path_str = make_string_path(new_path) new_path_normalized = self.absnormpath(new_path_str) if self.exists(new_path_normalized, check_link=True): self.raise_os_error(errno.EEXIST, new_path_str) new_parent_directory, new_basename = self.splitpath( new_path_normalized) if not new_parent_directory: new_parent_directory = matching_string(new_path_str, self.cwd) if not self.exists(new_parent_directory): if create_missing_dirs: self.create_dir(new_parent_directory) else: self.raise_os_error(errno.ENOENT, new_parent_directory) if self.ends_with_path_separator(old_path_str): error = errno.EINVAL if self.is_windows_fs else errno.ENOTDIR self.raise_os_error(error, old_path_str) if not self.is_windows_fs and self.ends_with_path_separator(new_path): self.raise_os_error(errno.ENOENT, old_path_str) # Retrieve the target file try: old_file = self.resolve(old_path_str, follow_symlinks=follow_symlinks) except OSError: self.raise_os_error(errno.ENOENT, old_path_str) if old_file.st_mode & S_IFDIR: self.raise_os_error( errno.EACCES if self.is_windows_fs else errno.EPERM, old_path_str ) # abuse the name field to control the filename of the # newly created link old_file.name = new_basename # type: ignore[assignment] self.add_object(new_parent_directory, old_file) return old_file def link(self, old_path: AnyPath, new_path: AnyPath, follow_symlinks: bool = True) -> FakeFile: """Create a hard link at new_path, pointing at old_path. Args: old_path: An existing link to the target file. new_path: The destination path to create a new link at. follow_symlinks: If False and old_path is a symlink, link the symlink instead of the object it points to. Returns: The FakeFile object referred to by old_path. Raises: OSError: if something already exists at new_path. OSError: if old_path is a directory. OSError: if the parent directory doesn't exist. """ return self.create_link(old_path, new_path, follow_symlinks, create_missing_dirs=False) def _is_circular_link(self, link_obj: FakeFile) -> bool: try: assert link_obj.contents self.resolve_path(link_obj.contents) except OSError as exc: return exc.errno == errno.ELOOP return False def readlink(self, path: AnyPath) -> str: """Read the target of a symlink. Args: path: symlink to read the target of. Returns: the string representing the path to which the symbolic link points. Raises: TypeError: if path is None OSError: (with errno=ENOENT) if path is not a valid path, or (with errno=EINVAL) if path is valid, but is not a symlink, or if the path ends with a path separator (Posix only) """ if path is None: raise TypeError link_path = make_string_path(path) link_obj = self.lresolve(link_path) if S_IFMT(link_obj.st_mode) != S_IFLNK: self.raise_os_error(errno.EINVAL, link_path) if self.ends_with_path_separator(link_path): if not self.is_windows_fs and self.exists(link_path): self.raise_os_error(errno.EINVAL, link_path) if not self.exists(link_obj.path): # type: ignore if self.is_windows_fs: error = errno.EINVAL elif self._is_circular_link(link_obj): if self.is_macos: return link_obj.path # type: ignore[return-value] error = errno.ELOOP else: error = errno.ENOENT self.raise_os_error(error, link_obj.path) assert link_obj.contents return link_obj.contents def makedir(self, dir_path: AnyPath, mode: int = PERM_DEF) -> None: """Create a leaf Fake directory. Args: dir_path: (str) Name of directory to create. Relative paths are assumed to be relative to '/'. mode: (int) Mode to create directory with. This argument defaults to 0o777. The umask is applied to this mode. Raises: OSError: if the directory name is invalid or parent directory is read only or as per :py:meth:`add_object`. """ dir_name = make_string_path(dir_path) ends_with_sep = self.ends_with_path_separator(dir_name) dir_name = self._path_without_trailing_separators(dir_name) if not dir_name: self.raise_os_error(errno.ENOENT, '') if self.is_windows_fs: dir_name = self.absnormpath(dir_name) parent_dir, _ = self.splitpath(dir_name) if parent_dir: base_dir = self.normpath(parent_dir) ellipsis = matching_string(parent_dir, self.path_separator + '..') if parent_dir.endswith(ellipsis) and not self.is_windows_fs: base_dir, dummy_dotdot, _ = parent_dir.partition(ellipsis) if not self.exists(base_dir): self.raise_os_error(errno.ENOENT, base_dir) dir_name = self.absnormpath(dir_name) if self.exists(dir_name, check_link=True): if self.is_windows_fs and dir_name == self.path_separator: error_nr = errno.EACCES else: error_nr = errno.EEXIST if ends_with_sep and self.is_macos and not self.exists(dir_name): # to avoid EEXIST exception, remove the link self.remove_object(dir_name) else: self.raise_os_error(error_nr, dir_name) head, tail = self.splitpath(dir_name) self.add_object( to_string(head), FakeDirectory(to_string(tail), mode & ~self.umask, filesystem=self)) def _path_without_trailing_separators(self, path: AnyStr) -> AnyStr: while self.ends_with_path_separator(path): path = path[:-1] return path def makedirs(self, dir_name: AnyStr, mode: int = PERM_DEF, exist_ok: bool = False) -> None: """Create a leaf Fake directory and create any non-existent parent dirs. Args: dir_name: (str) Name of directory to create. mode: (int) Mode to create directory (and any necessary parent directories) with. This argument defaults to 0o777. The umask is applied to this mode. exist_ok: (boolean) If exist_ok is False (the default), an OSError is raised if the target directory already exists. Raises: OSError: if the directory already exists and exist_ok=False, or as per :py:meth:`create_dir`. """ if not dir_name: self.raise_os_error(errno.ENOENT, '') ends_with_sep = self.ends_with_path_separator(dir_name) dir_name = self.absnormpath(dir_name) if (ends_with_sep and self.is_macos and self.exists(dir_name, check_link=True) and not self.exists(dir_name)): # to avoid EEXIST exception, remove the link self.remove_object(dir_name) dir_name_str = to_string(dir_name) path_components = self._path_components(dir_name_str) # Raise a permission denied error if the first existing directory # is not writeable. current_dir = self.root for component in path_components: if (not hasattr(current_dir, "entries") or component not in current_dir.entries): break else: current_dir = cast(FakeDirectory, current_dir.entries[component]) try: self.create_dir(dir_name, mode & ~self.umask) except OSError as e: if e.errno == errno.EACCES: # permission denied - propagate exception raise if (not exist_ok or not isinstance(self.resolve(dir_name), FakeDirectory)): if self.is_windows_fs and e.errno == errno.ENOTDIR: e.errno = errno.ENOENT self.raise_os_error(e.errno, e.filename) def _is_of_type(self, path: AnyPath, st_flag: int, follow_symlinks: bool = True, check_read_perm: bool = True) -> bool: """Helper function to implement isdir(), islink(), etc. See the stat(2) man page for valid stat.S_I* flag values Args: path: Path to file to stat and test st_flag: The stat.S_I* flag checked for the file's st_mode check_read_perm: If True (default) False is returned for existing but unreadable file paths. Returns: (boolean) `True` if the st_flag is set in path's st_mode. Raises: TypeError: if path is None """ if path is None: raise TypeError file_path = make_string_path(path) try: obj = self.resolve(file_path, follow_symlinks, check_read_perm=check_read_perm) if obj: self.raise_for_filepath_ending_with_separator( file_path, obj, macos_handling=not follow_symlinks) return S_IFMT(obj.st_mode) == st_flag except OSError: return False return False def isdir(self, path: AnyPath, follow_symlinks: bool = True) -> bool: """Determine if path identifies a directory. Args: path: Path to filesystem object. Returns: `True` if path points to a directory (following symlinks). Raises: TypeError: if path is None. """ return self._is_of_type(path, S_IFDIR, follow_symlinks) def isfile(self, path: AnyPath, follow_symlinks: bool = True) -> bool: """Determine if path identifies a regular file. Args: path: Path to filesystem object. Returns: `True` if path points to a regular file (following symlinks). Raises: TypeError: if path is None. """ return self._is_of_type(path, S_IFREG, follow_symlinks, check_read_perm=False) def islink(self, path: AnyPath) -> bool: """Determine if path identifies a symbolic link. Args: path: Path to filesystem object. Returns: `True` if path points to a symlink (S_IFLNK set in st_mode) Raises: TypeError: if path is None. """ return self._is_of_type(path, S_IFLNK, follow_symlinks=False) def confirmdir(self, target_directory: AnyStr) -> FakeDirectory: """Test that the target is actually a directory, raising OSError if not. Args: target_directory: Path to the target directory within the fake filesystem. Returns: The FakeDirectory object corresponding to target_directory. Raises: OSError: if the target is not a directory. """ directory = cast(FakeDirectory, self.resolve(target_directory)) if not directory.st_mode & S_IFDIR: self.raise_os_error(errno.ENOTDIR, target_directory, 267) return directory def remove(self, path: AnyStr) -> None: """Remove the FakeFile object at the specified file path. Args: path: Path to file to be removed. Raises: OSError: if path points to a directory. OSError: if path does not exist. OSError: if removal failed. """ norm_path = make_string_path(path) norm_path = self.absnormpath(norm_path) if self.ends_with_path_separator(path): self._handle_broken_link_with_trailing_sep(norm_path) if self.exists(norm_path): obj = self.resolve(norm_path, check_read_perm=False) if S_IFMT(obj.st_mode) == S_IFDIR: link_obj = self.lresolve(norm_path) if S_IFMT(link_obj.st_mode) != S_IFLNK: if self.is_windows_fs: error = errno.EACCES elif self.is_macos: error = errno.EPERM else: error = errno.EISDIR self.raise_os_error(error, norm_path) if path.endswith(matching_string(path, self.path_separator)): if self.is_windows_fs: error = errno.EACCES elif self.is_macos: error = errno.EPERM else: error = errno.ENOTDIR self.raise_os_error(error, norm_path) else: self.raise_for_filepath_ending_with_separator(path, obj) self.remove_object(norm_path) def rmdir(self, target_directory: AnyStr, allow_symlink: bool = False) -> None: """Remove a leaf Fake directory. Args: target_directory: (str) Name of directory to remove. allow_symlink: (bool) if `target_directory` is a symlink, the function just returns, otherwise it raises (Posix only) Raises: OSError: if target_directory does not exist. OSError: if target_directory does not point to a directory. OSError: if removal failed per FakeFilesystem.RemoveObject. Cannot remove '.'. """ if target_directory == matching_string(target_directory, '.'): error_nr = errno.EACCES if self.is_windows_fs else errno.EINVAL self.raise_os_error(error_nr, target_directory) ends_with_sep = self.ends_with_path_separator(target_directory) target_directory = self.absnormpath(target_directory) if self.confirmdir(target_directory): if not self.is_windows_fs and self.islink(target_directory): if allow_symlink: return if not ends_with_sep or not self.is_macos: self.raise_os_error(errno.ENOTDIR, target_directory) dir_object = self.resolve(target_directory) if dir_object.entries: self.raise_os_error(errno.ENOTEMPTY, target_directory) self.remove_object(target_directory) def listdir(self, target_directory: AnyStr) -> List[AnyStr]: """Return a list of file names in target_directory. Args: target_directory: Path to the target directory within the fake filesystem. Returns: A list of file names within the target directory in arbitrary order. If `shuffle_listdir_results` is set, the order is not the same in subsequent calls to avoid tests relying on any ordering. Raises: OSError: if the target is not a directory. """ target_directory = self.resolve_path(target_directory, allow_fd=True) directory = self.confirmdir(target_directory) directory_contents = list(directory.entries.keys()) if self.shuffle_listdir_results: random.shuffle(directory_contents) return directory_contents # type: ignore[return-value] def __str__(self) -> str: return str(self.root) def _add_standard_streams(self) -> None: self._add_open_file(StandardStreamWrapper(sys.stdin)) self._add_open_file(StandardStreamWrapper(sys.stdout)) self._add_open_file(StandardStreamWrapper(sys.stderr)) Deprecator.add(FakeFilesystem, FakeFilesystem.get_disk_usage, 'GetDiskUsage') Deprecator.add(FakeFilesystem, FakeFilesystem.set_disk_usage, 'SetDiskUsage') Deprecator.add(FakeFilesystem, FakeFilesystem.change_disk_usage, 'ChangeDiskUsage') Deprecator.add(FakeFilesystem, FakeFilesystem.add_mount_point, 'AddMountPoint') Deprecator.add(FakeFilesystem, FakeFilesystem.stat, 'GetStat') Deprecator.add(FakeFilesystem, FakeFilesystem.chmod, 'ChangeMode') Deprecator.add(FakeFilesystem, FakeFilesystem.utime, 'UpdateTime') Deprecator.add(FakeFilesystem, FakeFilesystem._add_open_file, 'AddOpenFile') Deprecator.add(FakeFilesystem, FakeFilesystem._close_open_file, 'CloseOpenFile') Deprecator.add(FakeFilesystem, FakeFilesystem.has_open_file, 'HasOpenFile') Deprecator.add(FakeFilesystem, FakeFilesystem.get_open_file, 'GetOpenFile') Deprecator.add(FakeFilesystem, FakeFilesystem.normcase, 'NormalizePathSeparator') Deprecator.add(FakeFilesystem, FakeFilesystem.normpath, 'CollapsePath') Deprecator.add(FakeFilesystem, FakeFilesystem._original_path, 'NormalizeCase') Deprecator.add(FakeFilesystem, FakeFilesystem.absnormpath, 'NormalizePath') Deprecator.add(FakeFilesystem, FakeFilesystem.splitpath, 'SplitPath') Deprecator.add(FakeFilesystem, FakeFilesystem.splitdrive, 'SplitDrive') Deprecator.add(FakeFilesystem, FakeFilesystem.joinpaths, 'JoinPaths') Deprecator.add(FakeFilesystem, FakeFilesystem._path_components, 'GetPathComponents') Deprecator.add(FakeFilesystem, FakeFilesystem._starts_with_drive_letter, 'StartsWithDriveLetter') Deprecator.add(FakeFilesystem, FakeFilesystem.exists, 'Exists') Deprecator.add(FakeFilesystem, FakeFilesystem.resolve_path, 'ResolvePath') Deprecator.add(FakeFilesystem, FakeFilesystem.get_object_from_normpath, 'GetObjectFromNormalizedPath') Deprecator.add(FakeFilesystem, FakeFilesystem.get_object, 'GetObject') Deprecator.add(FakeFilesystem, FakeFilesystem.resolve, 'ResolveObject') Deprecator.add(FakeFilesystem, FakeFilesystem.lresolve, 'LResolveObject') Deprecator.add(FakeFilesystem, FakeFilesystem.add_object, 'AddObject') Deprecator.add(FakeFilesystem, FakeFilesystem.remove_object, 'RemoveObject') Deprecator.add(FakeFilesystem, FakeFilesystem.rename, 'RenameObject') Deprecator.add(FakeFilesystem, FakeFilesystem.create_dir, 'CreateDirectory') Deprecator.add(FakeFilesystem, FakeFilesystem.create_file, 'CreateFile') Deprecator.add(FakeFilesystem, FakeFilesystem.create_symlink, 'CreateLink') Deprecator.add(FakeFilesystem, FakeFilesystem.link, 'CreateHardLink') Deprecator.add(FakeFilesystem, FakeFilesystem.readlink, 'ReadLink') Deprecator.add(FakeFilesystem, FakeFilesystem.makedir, 'MakeDirectory') Deprecator.add(FakeFilesystem, FakeFilesystem.makedirs, 'MakeDirectories') Deprecator.add(FakeFilesystem, FakeFilesystem.isdir, 'IsDir') Deprecator.add(FakeFilesystem, FakeFilesystem.isfile, 'IsFile') Deprecator.add(FakeFilesystem, FakeFilesystem.islink, 'IsLink') Deprecator.add(FakeFilesystem, FakeFilesystem.confirmdir, 'ConfirmDir') Deprecator.add(FakeFilesystem, FakeFilesystem.remove, 'RemoveFile') Deprecator.add(FakeFilesystem, FakeFilesystem.rmdir, 'RemoveDirectory') Deprecator.add(FakeFilesystem, FakeFilesystem.listdir, 'ListDir') class FakePathModule: """Faked os.path module replacement. FakePathModule should *only* be instantiated by FakeOsModule. See the FakeOsModule docstring for details. """ _OS_PATH_COPY: Any = _copy_module(os.path) devnull: ClassVar[str] = '' sep: ClassVar[str] = '' altsep: ClassVar[Optional[str]] = None linesep: ClassVar[str] = '' pathsep: ClassVar[str] = '' @staticmethod def dir() -> List[str]: """Return the list of patched function names. Used for patching functions imported from the module. """ return [ 'abspath', 'dirname', 'exists', 'expanduser', 'getatime', 'getctime', 'getmtime', 'getsize', 'isabs', 'isdir', 'isfile', 'islink', 'ismount', 'join', 'lexists', 'normcase', 'normpath', 'realpath', 'relpath', 'split', 'splitdrive', 'samefile' ] def __init__(self, filesystem: FakeFilesystem, os_module: 'FakeOsModule'): """Init. Args: filesystem: FakeFilesystem used to provide file system information """ self.filesystem = filesystem self._os_path = self._OS_PATH_COPY self._os_path.os = self.os = os_module # type: ignore[attr-defined] self.reset(filesystem) @classmethod def reset(cls, filesystem: FakeFilesystem) -> None: cls.sep = filesystem.path_separator cls.altsep = filesystem.alternative_path_separator cls.linesep = filesystem.line_separator() cls.devnull = 'nul' if filesystem.is_windows_fs else '/dev/null' cls.pathsep = ';' if filesystem.is_windows_fs else ':' def exists(self, path: AnyStr) -> bool: """Determine whether the file object exists within the fake filesystem. Args: path: The path to the file object. Returns: (bool) `True` if the file exists. """ return self.filesystem.exists(path) def lexists(self, path: AnyStr) -> bool: """Test whether a path exists. Returns True for broken symbolic links. Args: path: path to the symlink object. Returns: bool (if file exists). """ return self.filesystem.exists(path, check_link=True) def getsize(self, path: AnyStr): """Return the file object size in bytes. Args: path: path to the file object. Returns: file size in bytes. """ file_obj = self.filesystem.resolve(path) if (self.filesystem.ends_with_path_separator(path) and S_IFMT(file_obj.st_mode) != S_IFDIR): error_nr = (errno.EINVAL if self.filesystem.is_windows_fs else errno.ENOTDIR) self.filesystem.raise_os_error(error_nr, path) return file_obj.st_size def isabs(self, path: AnyStr) -> bool: """Return True if path is an absolute pathname.""" if self.filesystem.is_windows_fs: path = self.splitdrive(path)[1] path = make_string_path(path) return self.filesystem._starts_with_sep(path) def isdir(self, path: AnyStr) -> bool: """Determine if path identifies a directory.""" return self.filesystem.isdir(path) def isfile(self, path: AnyStr) -> bool: """Determine if path identifies a regular file.""" return self.filesystem.isfile(path) def islink(self, path: AnyStr) -> bool: """Determine if path identifies a symbolic link. Args: path: Path to filesystem object. Returns: `True` if path points to a symbolic link. Raises: TypeError: if path is None. """ return self.filesystem.islink(path) def getmtime(self, path: AnyStr) -> float: """Returns the modification time of the fake file. Args: path: the path to fake file. Returns: (int, float) the modification time of the fake file in number of seconds since the epoch. Raises: OSError: if the file does not exist. """ try: file_obj = self.filesystem.resolve(path) return file_obj.st_mtime except OSError: self.filesystem.raise_os_error(errno.ENOENT, winerror=3) def getatime(self, path: AnyStr) -> float: """Returns the last access time of the fake file. Note: Access time is not set automatically in fake filesystem on access. Args: path: the path to fake file. Returns: (int, float) the access time of the fake file in number of seconds since the epoch. Raises: OSError: if the file does not exist. """ try: file_obj = self.filesystem.resolve(path) except OSError: self.filesystem.raise_os_error(errno.ENOENT) return file_obj.st_atime def getctime(self, path: AnyStr) -> float: """Returns the creation time of the fake file. Args: path: the path to fake file. Returns: (int, float) the creation time of the fake file in number of seconds since the epoch. Raises: OSError: if the file does not exist. """ try: file_obj = self.filesystem.resolve(path) except OSError: self.filesystem.raise_os_error(errno.ENOENT) return file_obj.st_ctime def abspath(self, path: AnyStr) -> AnyStr: """Return the absolute version of a path.""" def getcwd(): """Return the current working directory.""" # pylint: disable=undefined-variable if isinstance(path, bytes): return self.os.getcwdb() else: return self.os.getcwd() path = make_string_path(path) if not self.isabs(path): path = self.join(getcwd(), path) elif (self.filesystem.is_windows_fs and self.filesystem._starts_with_sep(path)): cwd = getcwd() if self.filesystem._starts_with_drive_letter(cwd): path = self.join(cwd[:2], path) return self.normpath(path) def join(self, *p: AnyStr) -> AnyStr: """Return the completed path with a separator of the parts.""" return self.filesystem.joinpaths(*p) def split(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]: """Split the path into the directory and the filename of the path. """ return self.filesystem.splitpath(path) def splitdrive(self, path: AnyStr) -> Tuple[AnyStr, AnyStr]: """Split the path into the drive part and the rest of the path, if supported.""" return self.filesystem.splitdrive(path) def normpath(self, path: AnyStr) -> AnyStr: """Normalize path, eliminating double slashes, etc.""" return self.filesystem.normpath(path) def normcase(self, path: AnyStr) -> AnyStr: """Convert to lower case under windows, replaces additional path separator.""" path = self.filesystem.normcase(path) if self.filesystem.is_windows_fs: path = path.lower() return path def relpath(self, path: AnyStr, start: Optional[AnyStr] = None) -> AnyStr: """We mostly rely on the native implementation and adapt the path separator.""" if not path: raise ValueError("no path specified") path = make_string_path(path) if start is not None: start = make_string_path(start) else: start = matching_string(path, self.filesystem.cwd) system_sep = matching_string(path, self._os_path.sep) if self.filesystem.alternative_path_separator is not None: altsep = matching_string( path, self.filesystem.alternative_path_separator) path = path.replace(altsep, system_sep) start = start.replace(altsep, system_sep) sep = matching_string(path, self.filesystem.path_separator) path = path.replace(sep, system_sep) start = start.replace(sep, system_sep) path = self._os_path.relpath(path, start) return path.replace(system_sep, sep) def realpath(self, filename: AnyStr, strict: bool = None) -> AnyStr: """Return the canonical path of the specified filename, eliminating any symbolic links encountered in the path. """ if strict is not None and sys.version_info < (3, 10): raise TypeError("realpath() got an unexpected " "keyword argument 'strict'") if strict: # raises in strict mode if the file does not exist self.filesystem.resolve(filename) if self.filesystem.is_windows_fs: return self.abspath(filename) filename = make_string_path(filename) path, ok = self._join_real_path(filename[:0], filename, {}) path = self.abspath(path) return path def samefile(self, path1: AnyStr, path2: AnyStr) -> bool: """Return whether path1 and path2 point to the same file. Args: path1: first file path or path object (Python >=3.6) path2: second file path or path object (Python >=3.6) Raises: OSError: if one of the paths does not point to an existing file system object. """ stat1 = self.filesystem.stat(path1) stat2 = self.filesystem.stat(path2) return (stat1.st_ino == stat2.st_ino and stat1.st_dev == stat2.st_dev) @overload def _join_real_path( self, path: str, rest: str, seen: Dict[str, Optional[str]]) -> Tuple[str, bool]: ... @overload def _join_real_path( self, path: bytes, rest: bytes, seen: Dict[bytes, Optional[bytes]]) -> Tuple[bytes, bool]: ... def _join_real_path( self, path: AnyStr, rest: AnyStr, seen: Dict[AnyStr, Optional[AnyStr]]) -> Tuple[AnyStr, bool]: """Join two paths, normalizing and eliminating any symbolic links encountered in the second path. Taken from Python source and adapted. """ curdir = matching_string(path, '.') pardir = matching_string(path, '..') sep = self.filesystem.get_path_separator(path) if self.isabs(rest): rest = rest[1:] path = sep while rest: name, _, rest = rest.partition(sep) if not name or name == curdir: # current dir continue if name == pardir: # parent dir if path: path, name = self.filesystem.splitpath(path) if name == pardir: path = self.filesystem.joinpaths(path, pardir, pardir) else: path = pardir continue newpath = self.filesystem.joinpaths(path, name) if not self.filesystem.islink(newpath): path = newpath continue # Resolve the symbolic link if newpath in seen: # Already seen this path seen_path = seen[newpath] if seen_path is not None: # use cached value path = seen_path continue # The symlink is not resolved, so we must have a symlink loop. # Return already resolved part + rest of the path unchanged. return self.filesystem.joinpaths(newpath, rest), False seen[newpath] = None # not resolved symlink path, ok = self._join_real_path( path, matching_string(path, self.filesystem.readlink( newpath)), seen) if not ok: return self.filesystem.joinpaths(path, rest), False seen[newpath] = path # resolved symlink return path, True def dirname(self, path: AnyStr) -> AnyStr: """Returns the first part of the result of `split()`.""" return self.split(path)[0] def expanduser(self, path: AnyStr) -> AnyStr: """Return the argument with an initial component of ~ or ~user replaced by that user's home directory. """ path = self._os_path.expanduser(path) return path.replace( matching_string(path, self._os_path.sep), matching_string(path, self.sep)) def ismount(self, path: AnyStr) -> bool: """Return true if the given path is a mount point. Args: path: Path to filesystem object to be checked Returns: `True` if path is a mount point added to the fake file system. Under Windows also returns True for drive and UNC roots (independent of their existence). """ if not path: return False path_str = to_string(make_string_path(path)) normed_path = self.filesystem.absnormpath(path_str) sep = self.filesystem.path_separator if self.filesystem.is_windows_fs: path_seps: Union[Tuple[str, Optional[str]], Tuple[str]] if self.filesystem.alternative_path_separator is not None: path_seps = ( sep, self.filesystem.alternative_path_separator ) else: path_seps = (sep,) drive, rest = self.filesystem.splitdrive(normed_path) if drive and drive[:1] in path_seps: return (not rest) or (rest in path_seps) if rest in path_seps: return True for mount_point in self.filesystem.mount_points: if (to_string(normed_path).rstrip(sep) == to_string(mount_point).rstrip(sep)): return True return False def __getattr__(self, name: str) -> Any: """Forwards any non-faked calls to the real os.path.""" return getattr(self._os_path, name) class FakeOsModule: """Uses FakeFilesystem to provide a fake os module replacement. Do not create os.path separately from os, as there is a necessary circular dependency between os and os.path to replicate the behavior of the standard Python modules. What you want to do is to just let FakeOsModule take care of `os.path` setup itself. # You always want to do this. filesystem = fake_filesystem.FakeFilesystem() my_os_module = fake_filesystem.FakeOsModule(filesystem) """ @staticmethod def dir() -> List[str]: """Return the list of patched function names. Used for patching functions imported from the module. """ _dir = [ 'access', 'chdir', 'chmod', 'chown', 'close', 'fstat', 'fsync', 'getcwd', 'lchmod', 'link', 'listdir', 'lstat', 'makedirs', 'mkdir', 'mknod', 'open', 'read', 'readlink', 'remove', 'removedirs', 'rename', 'rmdir', 'stat', 'symlink', 'umask', 'unlink', 'utime', 'walk', 'write', 'getcwdb', 'replace' ] if sys.platform.startswith('linux'): _dir += [ 'fdatasync', 'getxattr', 'listxattr', 'removexattr', 'setxattr' ] if use_scandir: _dir += ['scandir'] return _dir def __init__(self, filesystem: FakeFilesystem): """Also exposes self.path (to fake os.path). Args: filesystem: FakeFilesystem used to provide file system information """ self.filesystem = filesystem self._os_module: Any = os self.path = FakePathModule(self.filesystem, self) @property def devnull(self) -> str: return self.path.devnull @property def sep(self) -> str: return self.path.sep @property def altsep(self) -> Optional[str]: return self.path.altsep @property def linesep(self) -> str: return self.path.linesep @property def pathsep(self) -> str: return self.path.pathsep def fdopen(self, fd: int, *args: Any, **kwargs: Any) -> AnyFileWrapper: """Redirector to open() builtin function. Args: fd: The file descriptor of the file to open. *args: Pass through args. **kwargs: Pass through kwargs. Returns: File object corresponding to file_des. Raises: TypeError: if file descriptor is not an integer. """ if not is_int_type(fd): raise TypeError('an integer is required') return FakeFileOpen(self.filesystem)(fd, *args, **kwargs) def _umask(self) -> int: """Return the current umask.""" if self.filesystem.is_windows_fs: # windows always returns 0 - it has no real notion of umask return 0 if sys.platform == 'win32': # if we are testing Unix under Windows we assume a default mask return 0o002 else: # under Unix, we return the real umask; # as there is no pure getter for umask, so we have to first # set a mode to get the previous one and then re-set that mask = os.umask(0) os.umask(mask) return mask def open(self, path: AnyStr, flags: int, mode: Optional[int] = None, *, dir_fd: Optional[int] = None) -> int: """Return the file descriptor for a FakeFile. Args: path: the path to the file flags: low-level bits to indicate io operation mode: bits to define default permissions Note: only basic modes are supported, OS-specific modes are ignored dir_fd: If not `None`, the file descriptor of a directory, with `file_path` being relative to this directory. Returns: A file descriptor. Raises: OSError: if the path cannot be found ValueError: if invalid mode is given NotImplementedError: if `os.O_EXCL` is used without `os.O_CREAT` """ path = self._path_with_dir_fd(path, self.open, dir_fd) if mode is None: if self.filesystem.is_windows_fs: mode = 0o666 else: mode = 0o777 & ~self._umask() has_tmpfile_flag = (hasattr(os, 'O_TMPFILE') and flags & getattr(os, 'O_TMPFILE')) open_modes = _OpenModes( must_exist=not flags & os.O_CREAT and not has_tmpfile_flag, can_read=not flags & os.O_WRONLY, can_write=flags & (os.O_RDWR | os.O_WRONLY) != 0, truncate=flags & os.O_TRUNC != 0, append=flags & os.O_APPEND != 0, must_not_exist=flags & os.O_EXCL != 0 ) if open_modes.must_not_exist and open_modes.must_exist: raise NotImplementedError( 'O_EXCL without O_CREAT mode is not supported') if has_tmpfile_flag: # this is a workaround for tempfiles that do not have a filename # as we do not support this directly, we just add a unique filename # and set the file to delete on close path = self.filesystem.joinpaths( path, matching_string(path, str(uuid.uuid4()))) if (not self.filesystem.is_windows_fs and self.filesystem.exists(path)): # handle opening directory - only allowed under Posix # with read-only mode obj = self.filesystem.resolve(path) if isinstance(obj, FakeDirectory): if ((not open_modes.must_exist and not self.filesystem.is_macos) or open_modes.can_write): self.filesystem.raise_os_error(errno.EISDIR, path) dir_wrapper = FakeDirWrapper(obj, path, self.filesystem) file_des = self.filesystem._add_open_file(dir_wrapper) dir_wrapper.filedes = file_des return file_des # low level open is always binary str_flags = 'b' delete_on_close = has_tmpfile_flag if hasattr(os, 'O_TEMPORARY'): delete_on_close = flags & os.O_TEMPORARY == os.O_TEMPORARY fake_file = FakeFileOpen( self.filesystem, delete_on_close=delete_on_close, raw_io=True)( path, str_flags, open_modes=open_modes) assert not isinstance(fake_file, StandardStreamWrapper) if fake_file.file_object != self.filesystem.dev_null: self.chmod(path, mode) return fake_file.fileno() def close(self, fd: int) -> None: """Close a file descriptor. Args: fd: An integer file descriptor for the file object requested. Raises: OSError: bad file descriptor. TypeError: if file descriptor is not an integer. """ file_handle = self.filesystem.get_open_file(fd) file_handle.close() def read(self, fd: int, n: int) -> bytes: """Read number of bytes from a file descriptor, returns bytes read. Args: fd: An integer file descriptor for the file object requested. n: Number of bytes to read from file. Returns: Bytes read from file. Raises: OSError: bad file descriptor. TypeError: if file descriptor is not an integer. """ file_handle = self.filesystem.get_open_file(fd) if isinstance(file_handle, FakeFileWrapper): file_handle.raw_io = True if isinstance(file_handle, FakeDirWrapper): self.filesystem.raise_os_error(errno.EBADF, file_handle.file_path) return file_handle.read(n) def write(self, fd: int, contents: bytes) -> int: """Write string to file descriptor, returns number of bytes written. Args: fd: An integer file descriptor for the file object requested. contents: String of bytes to write to file. Returns: Number of bytes written. Raises: OSError: bad file descriptor. TypeError: if file descriptor is not an integer. """ file_handle = cast(FakeFileWrapper, self.filesystem.get_open_file(fd)) if isinstance(file_handle, FakeDirWrapper): self.filesystem.raise_os_error(errno.EBADF, file_handle.file_path) if isinstance(file_handle, FakePipeWrapper): return file_handle.write(contents) file_handle.raw_io = True file_handle._sync_io() file_handle.update_flush_pos() file_handle.write(contents) file_handle.flush() return len(contents) def pipe(self) -> Tuple[int, int]: read_fd, write_fd = os.pipe() read_wrapper = FakePipeWrapper(self.filesystem, read_fd, False) file_des = self.filesystem._add_open_file(read_wrapper) read_wrapper.filedes = file_des write_wrapper = FakePipeWrapper(self.filesystem, write_fd, True) file_des = self.filesystem._add_open_file(write_wrapper) write_wrapper.filedes = file_des return read_wrapper.filedes, write_wrapper.filedes @staticmethod def stat_float_times(newvalue: Optional[bool] = None) -> bool: """Determine whether a file's time stamps are reported as floats or ints. Calling without arguments returns the current value. The value is shared by all instances of FakeOsModule. Args: newvalue: If `True`, mtime, ctime, atime are reported as floats. Otherwise, they are returned as ints (rounding down). """ return FakeStatResult.stat_float_times(newvalue) def fstat(self, fd: int) -> FakeStatResult: """Return the os.stat-like tuple for the FakeFile object of file_des. Args: fd: The file descriptor of filesystem object to retrieve. Returns: The FakeStatResult object corresponding to entry_path. Raises: OSError: if the filesystem object doesn't exist. """ # stat should return the tuple representing return value of os.stat file_object = self.filesystem.get_open_file(fd).get_object() assert isinstance(file_object, FakeFile) return file_object.stat_result.copy() def umask(self, mask: int) -> int: """Change the current umask. Args: mask: (int) The new umask value. Returns: The old umask. Raises: TypeError: if new_mask is of an invalid type. """ if not is_int_type(mask): raise TypeError('an integer is required') old_umask = self.filesystem.umask self.filesystem.umask = mask return old_umask def chdir(self, path: AnyStr) -> None: """Change current working directory to target directory. Args: path: The path to new current working directory. Raises: OSError: if user lacks permission to enter the argument directory or if the target is not a directory. """ try: path = self.filesystem.resolve_path( path, allow_fd=True) except OSError as exc: if self.filesystem.is_macos and exc.errno == errno.EBADF: raise OSError(errno.ENOTDIR, "Not a directory: " + str(path)) raise self.filesystem.confirmdir(path) directory = self.filesystem.resolve(path) # A full implementation would check permissions all the way # up the tree. if not is_root() and not directory.st_mode | PERM_EXE: self.filesystem.raise_os_error(errno.EACCES, directory.name) self.filesystem.cwd = path # type: ignore[assignment] def getcwd(self) -> str: """Return current working directory.""" return to_string(self.filesystem.cwd) def getcwdb(self) -> bytes: """Return current working directory as bytes.""" return to_bytes(self.filesystem.cwd) def listdir(self, path: AnyStr) -> List[AnyStr]: """Return a list of file names in target_directory. Args: path: Path to the target directory within the fake filesystem. Returns: A list of file names within the target directory in arbitrary order. Raises: OSError: if the target is not a directory. """ return self.filesystem.listdir(path) XATTR_CREATE = 1 XATTR_REPLACE = 2 def getxattr(self, path: AnyStr, attribute: AnyString, *, follow_symlinks: bool = True) -> Optional[bytes]: """Return the value of the given extended filesystem attribute for `path`. Args: path: File path, file descriptor or path-like object (for Python >= 3.6). attribute: (str or bytes) The attribute name. follow_symlinks: (bool) If True (the default), symlinks in the path are traversed. Returns: The contents of the extended attribute as bytes or None if the attribute does not exist. Raises: OSError: if the path does not exist. """ if not self.filesystem.is_linux: raise AttributeError( "module 'os' has no attribute 'getxattr'") if isinstance(attribute, bytes): attribute = attribute.decode(sys.getfilesystemencoding()) file_obj = self.filesystem.resolve(path, follow_symlinks, allow_fd=True) return file_obj.xattr.get(attribute) def listxattr(self, path: Optional[AnyStr] = None, *, follow_symlinks: bool = True) -> List[str]: """Return a list of the extended filesystem attributes on `path`. Args: path: File path, file descriptor or path-like object (for Python >= 3.6). If None, the current directory is used. follow_symlinks: (bool) If True (the default), symlinks in the path are traversed. Returns: A list of all attribute names for the given path as str. Raises: OSError: if the path does not exist. """ if not self.filesystem.is_linux: raise AttributeError( "module 'os' has no attribute 'listxattr'") path_str = self.filesystem.cwd if path is None else path file_obj = self.filesystem.resolve( cast(AnyStr, path_str), follow_symlinks, allow_fd=True) return list(file_obj.xattr.keys()) def removexattr(self, path: AnyStr, attribute: AnyString, *, follow_symlinks: bool = True) -> None: """Removes the extended filesystem attribute attribute from `path`. Args: path: File path, file descriptor or path-like object (for Python >= 3.6). attribute: (str or bytes) The attribute name. follow_symlinks: (bool) If True (the default), symlinks in the path are traversed. Raises: OSError: if the path does not exist. """ if not self.filesystem.is_linux: raise AttributeError( "module 'os' has no attribute 'removexattr'") if isinstance(attribute, bytes): attribute = attribute.decode(sys.getfilesystemencoding()) file_obj = self.filesystem.resolve(path, follow_symlinks, allow_fd=True) if attribute in file_obj.xattr: del file_obj.xattr[attribute] def setxattr(self, path: AnyStr, attribute: AnyString, value: bytes, flags: int = 0, *, follow_symlinks: bool = True) -> None: """Sets the value of the given extended filesystem attribute for `path`. Args: path: File path, file descriptor or path-like object (for Python >= 3.6). attribute: The attribute name (str or bytes). value: (byte-like) The value to be set. follow_symlinks: (bool) If True (the default), symlinks in the path are traversed. Raises: OSError: if the path does not exist. TypeError: if `value` is not a byte-like object. """ if not self.filesystem.is_linux: raise AttributeError( "module 'os' has no attribute 'setxattr'") if isinstance(attribute, bytes): attribute = attribute.decode(sys.getfilesystemencoding()) if not is_byte_string(value): raise TypeError('a bytes-like object is required') file_obj = self.filesystem.resolve(path, follow_symlinks, allow_fd=True) exists = attribute in file_obj.xattr if exists and flags == self.XATTR_CREATE: self.filesystem.raise_os_error(errno.ENODATA, file_obj.path) if not exists and flags == self.XATTR_REPLACE: self.filesystem.raise_os_error(errno.EEXIST, file_obj.path) file_obj.xattr[attribute] = value def scandir(self, path: str = '.') -> ScanDirIter: """Return an iterator of DirEntry objects corresponding to the entries in the directory given by path. Args: path: Path to the target directory within the fake filesystem. Returns: An iterator to an unsorted list of os.DirEntry objects for each entry in path. Raises: OSError: if the target is not a directory. """ return scandir(self.filesystem, path) def walk(self, top: AnyStr, topdown: bool = True, onerror: Optional[bool] = None, followlinks: bool = False): """Perform an os.walk operation over the fake filesystem. Args: top: The root directory from which to begin walk. topdown: Determines whether to return the tuples with the root as the first entry (`True`) or as the last, after all the child directory tuples (`False`). onerror: If not `None`, function which will be called to handle the `os.error` instance provided when `os.listdir()` fails. followlinks: If `True`, symbolic links are followed. Yields: (path, directories, nondirectories) for top and each of its subdirectories. See the documentation for the builtin os module for further details. """ return walk(self.filesystem, top, topdown, onerror, followlinks) def readlink(self, path: AnyStr, dir_fd: Optional[int] = None) -> str: """Read the target of a symlink. Args: path: Symlink to read the target of. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. Returns: the string representing the path to which the symbolic link points. Raises: TypeError: if `path` is None OSError: (with errno=ENOENT) if path is not a valid path, or (with errno=EINVAL) if path is valid, but is not a symlink """ path = self._path_with_dir_fd(path, self.readlink, dir_fd) return self.filesystem.readlink(path) def stat(self, path: AnyStr, *, dir_fd: Optional[int] = None, follow_symlinks: bool = True) -> FakeStatResult: """Return the os.stat-like tuple for the FakeFile object of entry_path. Args: path: path to filesystem object to retrieve. dir_fd: (int) If not `None`, the file descriptor of a directory, with `entry_path` being relative to this directory. follow_symlinks: (bool) If `False` and `entry_path` points to a symlink, the link itself is changed instead of the linked object. Returns: The FakeStatResult object corresponding to entry_path. Raises: OSError: if the filesystem object doesn't exist. """ path = self._path_with_dir_fd(path, self.stat, dir_fd) return self.filesystem.stat(path, follow_symlinks) def lstat(self, path: AnyStr, *, dir_fd: Optional[int] = None) -> FakeStatResult: """Return the os.stat-like tuple for entry_path, not following symlinks. Args: path: path to filesystem object to retrieve. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. Returns: the FakeStatResult object corresponding to `path`. Raises: OSError: if the filesystem object doesn't exist. """ # stat should return the tuple representing return value of os.stat path = self._path_with_dir_fd(path, self.lstat, dir_fd) return self.filesystem.stat(path, follow_symlinks=False) def remove(self, path: AnyStr, dir_fd: Optional[int] = None) -> None: """Remove the FakeFile object at the specified file path. Args: path: Path to file to be removed. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. Raises: OSError: if path points to a directory. OSError: if path does not exist. OSError: if removal failed. """ path = self._path_with_dir_fd(path, self.remove, dir_fd) self.filesystem.remove(path) def unlink(self, path: AnyStr, *, dir_fd: Optional[int] = None) -> None: """Remove the FakeFile object at the specified file path. Args: path: Path to file to be removed. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. Raises: OSError: if path points to a directory. OSError: if path does not exist. OSError: if removal failed. """ path = self._path_with_dir_fd(path, self.unlink, dir_fd) self.filesystem.remove(path) def rename(self, src: AnyStr, dst: AnyStr, *, src_dir_fd: Optional[int] = None, dst_dir_fd: Optional[int] = None) -> None: """Rename a FakeFile object at old_file_path to new_file_path, preserving all properties. Also replaces existing new_file_path object, if one existed (Unix only). Args: src: Path to filesystem object to rename. dst: Path to where the filesystem object will live after this call. src_dir_fd: If not `None`, the file descriptor of a directory, with `src` being relative to this directory. dst_dir_fd: If not `None`, the file descriptor of a directory, with `dst` being relative to this directory. Raises: OSError: if old_file_path does not exist. OSError: if new_file_path is an existing directory. OSError: if new_file_path is an existing file (Windows only) OSError: if new_file_path is an existing file and could not be removed (Unix) OSError: if `dirname(new_file)` does not exist OSError: if the file would be moved to another filesystem (e.g. mount point) """ src = self._path_with_dir_fd(src, self.rename, src_dir_fd) dst = self._path_with_dir_fd(dst, self.rename, dst_dir_fd) self.filesystem.rename(src, dst) def replace(self, src: AnyStr, dst: AnyStr, *, src_dir_fd: Optional[int] = None, dst_dir_fd: Optional[int] = None) -> None: """Renames a FakeFile object at old_file_path to new_file_path, preserving all properties. Also replaces existing new_file_path object, if one existed. Arg src: Path to filesystem object to rename. dst: Path to where the filesystem object will live after this call. src_dir_fd: If not `None`, the file descriptor of a directory, with `src` being relative to this directory. dst_dir_fd: If not `None`, the file descriptor of a directory, with `dst` being relative to this directory. Raises: OSError: if old_file_path does not exist. OSError: if new_file_path is an existing directory. OSError: if new_file_path is an existing file and could not be removed OSError: if `dirname(new_file)` does not exist OSError: if the file would be moved to another filesystem (e.g. mount point) """ src = self._path_with_dir_fd(src, self.rename, src_dir_fd) dst = self._path_with_dir_fd(dst, self.rename, dst_dir_fd) self.filesystem.rename(src, dst, force_replace=True) def rmdir(self, path: AnyStr, *, dir_fd: Optional[int] = None) -> None: """Remove a leaf Fake directory. Args: path: (str) Name of directory to remove. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. Raises: OSError: if `path` does not exist or is not a directory, or as per FakeFilesystem.remove_object. Cannot remove '.'. """ path = self._path_with_dir_fd(path, self.rmdir, dir_fd) self.filesystem.rmdir(path) def removedirs(self, name: AnyStr) -> None: """Remove a leaf fake directory and all empty intermediate ones. Args: name: the directory to be removed. Raises: OSError: if target_directory does not exist or is not a directory. OSError: if target_directory is not empty. """ name = self.filesystem.absnormpath(name) directory = self.filesystem.confirmdir(name) if directory.entries: self.filesystem.raise_os_error( errno.ENOTEMPTY, self.path.basename(name)) else: self.rmdir(name) head, tail = self.path.split(name) if not tail: head, tail = self.path.split(head) while head and tail: head_dir = self.filesystem.confirmdir(head) if head_dir.entries: break # only the top-level dir may not be a symlink self.filesystem.rmdir(head, allow_symlink=True) head, tail = self.path.split(head) def mkdir(self, path: AnyStr, mode: int = PERM_DEF, *, dir_fd: Optional[int] = None) -> None: """Create a leaf Fake directory. Args: path: (str) Name of directory to create. Relative paths are assumed to be relative to '/'. mode: (int) Mode to create directory with. This argument defaults to 0o777. The umask is applied to this mode. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. Raises: OSError: if the directory name is invalid or parent directory is read only or as per FakeFilesystem.add_object. """ path = self._path_with_dir_fd(path, self.mkdir, dir_fd) try: self.filesystem.makedir(path, mode) except OSError as e: if e.errno == errno.EACCES: self.filesystem.raise_os_error(e.errno, path) raise def makedirs(self, name: AnyStr, mode: int = PERM_DEF, exist_ok: bool = None) -> None: """Create a leaf Fake directory + create any non-existent parent dirs. Args: name: (str) Name of directory to create. mode: (int) Mode to create directory (and any necessary parent directories) with. This argument defaults to 0o777. The umask is applied to this mode. exist_ok: (boolean) If exist_ok is False (the default), an OSError is raised if the target directory already exists. Raises: OSError: if the directory already exists and exist_ok=False, or as per :py:meth:`FakeFilesystem.create_dir`. """ if exist_ok is None: exist_ok = False self.filesystem.makedirs(name, mode, exist_ok) def _path_with_dir_fd(self, path: AnyStr, fct: Callable, dir_fd: Optional[int]) -> AnyStr: """Return the path considering dir_fd. Raise on invalid parameters.""" try: path = make_string_path(path) except TypeError: # the error is handled later path = path if dir_fd is not None: # check if fd is supported for the built-in real function real_fct = getattr(os, fct.__name__) if real_fct not in self.supports_dir_fd: raise NotImplementedError( 'dir_fd unavailable on this platform') if isinstance(path, int): raise ValueError("%s: Can't specify dir_fd without " "matching path_str" % fct.__name__) if not self.path.isabs(path): open_file = self.filesystem.get_open_file(dir_fd) return self.path.join( # type: ignore[type-var, return-value] cast(FakeFile, open_file.get_object()).path, path) return path def truncate(self, path: AnyStr, length: int) -> None: """Truncate the file corresponding to path, so that it is length bytes in size. If length is larger than the current size, the file is filled up with zero bytes. Args: path: (str or int) Path to the file, or an integer file descriptor for the file object. length: (int) Length of the file after truncating it. Raises: OSError: if the file does not exist or the file descriptor is invalid. """ file_object = self.filesystem.resolve(path, allow_fd=True) file_object.size = length def ftruncate(self, fd: int, length: int) -> None: """Truncate the file corresponding to fd, so that it is length bytes in size. If length is larger than the current size, the file is filled up with zero bytes. Args: fd: (int) File descriptor for the file object. length: (int) Maximum length of the file after truncating it. Raises: OSError: if the file descriptor is invalid """ file_object = self.filesystem.get_open_file(fd).get_object() if isinstance(file_object, FakeFileWrapper): file_object.size = length else: raise OSError(errno.EBADF, 'Invalid file descriptor') def access(self, path: AnyStr, mode: int, *, dir_fd: Optional[int] = None, effective_ids: bool = False, follow_symlinks: bool = True) -> bool: """Check if a file exists and has the specified permissions. Args: path: (str) Path to the file. mode: (int) Permissions represented as a bitwise-OR combination of os.F_OK, os.R_OK, os.W_OK, and os.X_OK. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. effective_ids: (bool) Unused. Only here to match the signature. follow_symlinks: (bool) If `False` and `path` points to a symlink, the link itself is queried instead of the linked object. Returns: bool, `True` if file is accessible, `False` otherwise. """ if effective_ids and self.filesystem.is_windows_fs: raise NotImplementedError( 'access: effective_ids unavailable on this platform') path = self._path_with_dir_fd(path, self.access, dir_fd) try: stat_result = self.stat(path, follow_symlinks=follow_symlinks) except OSError as os_error: if os_error.errno == errno.ENOENT: return False raise if is_root(): mode &= ~os.W_OK return (mode & ((stat_result.st_mode >> 6) & 7)) == mode def chmod(self, path: AnyStr, mode: int, *, dir_fd: Optional[int] = None, follow_symlinks: bool = True) -> None: """Change the permissions of a file as encoded in integer mode. Args: path: (str) Path to the file. mode: (int) Permissions. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. follow_symlinks: (bool) If `False` and `path` points to a symlink, the link itself is queried instead of the linked object. """ if (not follow_symlinks and (os.chmod not in os.supports_follow_symlinks or IS_PYPY)): raise NotImplementedError( "`follow_symlinks` for chmod() is not available " "on this system") path = self._path_with_dir_fd(path, self.chmod, dir_fd) self.filesystem.chmod(path, mode, follow_symlinks) def lchmod(self, path: AnyStr, mode: int) -> None: """Change the permissions of a file as encoded in integer mode. If the file is a link, the permissions of the link are changed. Args: path: (str) Path to the file. mode: (int) Permissions. """ if self.filesystem.is_windows_fs: raise NameError("name 'lchmod' is not defined") self.filesystem.chmod(path, mode, follow_symlinks=False) def utime(self, path: AnyStr, times: Optional[Tuple[Union[int, float], Union[int, float]]] = None, ns: Optional[Tuple[int, int]] = None, dir_fd: Optional[int] = None, follow_symlinks: bool = True) -> None: """Change the access and modified times of a file. Args: path: (str) Path to the file. times: 2-tuple of int or float numbers, of the form (atime, mtime) which is used to set the access and modified times in seconds. If None, both times are set to the current time. ns: 2-tuple of int numbers, of the form (atime, mtime) which is used to set the access and modified times in nanoseconds. If None, both times are set to the current time. dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. follow_symlinks: (bool) If `False` and `path` points to a symlink, the link itself is queried instead of the linked object. Raises: TypeError: If anything other than the expected types is specified in the passed `times` or `ns` tuple, or if the tuple length is not equal to 2. ValueError: If both times and ns are specified. """ path = self._path_with_dir_fd(path, self.utime, dir_fd) self.filesystem.utime( path, times=times, ns=ns, follow_symlinks=follow_symlinks) def chown(self, path: AnyStr, uid: int, gid: int, *, dir_fd: Optional[int] = None, follow_symlinks: bool = True) -> None: """Set ownership of a faked file. Args: path: (str) Path to the file or directory. uid: (int) Numeric uid to set the file or directory to. gid: (int) Numeric gid to set the file or directory to. dir_fd: (int) If not `None`, the file descriptor of a directory, with `path` being relative to this directory. follow_symlinks: (bool) If `False` and path points to a symlink, the link itself is changed instead of the linked object. Raises: OSError: if path does not exist. `None` is also allowed for `uid` and `gid`. This permits `os.rename` to use `os.chown` even when the source file `uid` and `gid` are `None` (unset). """ path = self._path_with_dir_fd(path, self.chown, dir_fd) file_object = self.filesystem.resolve( path, follow_symlinks, allow_fd=True) if not ((is_int_type(uid) or uid is None) and (is_int_type(gid) or gid is None)): raise TypeError("An integer is required") if uid != -1: file_object.st_uid = uid if gid != -1: file_object.st_gid = gid def mknod(self, path: AnyStr, mode: Optional[int] = None, device: int = 0, *, dir_fd: Optional[int] = None) -> None: """Create a filesystem node named 'filename'. Does not support device special files or named pipes as the real os module does. Args: path: (str) Name of the file to create mode: (int) Permissions to use and type of file to be created. Default permissions are 0o666. Only the stat.S_IFREG file type is supported by the fake implementation. The umask is applied to this mode. device: not supported in fake implementation dir_fd: If not `None`, the file descriptor of a directory, with `path` being relative to this directory. Raises: OSError: if called with unsupported options or the file can not be created. """ if self.filesystem.is_windows_fs: raise AttributeError("module 'os' has no attribute 'mknode'") if mode is None: # note that a default value of 0o600 without a device type is # documented - this is not how it seems to work mode = S_IFREG | 0o600 if device or not mode & S_IFREG and not is_root(): self.filesystem.raise_os_error(errno.EPERM) path = self._path_with_dir_fd(path, self.mknod, dir_fd) head, tail = self.path.split(path) if not tail: if self.filesystem.exists(head, check_link=True): self.filesystem.raise_os_error(errno.EEXIST, path) self.filesystem.raise_os_error(errno.ENOENT, path) if tail in (matching_string(tail, '.'), matching_string(tail, '..')): self.filesystem.raise_os_error(errno.ENOENT, path) if self.filesystem.exists(path, check_link=True): self.filesystem.raise_os_error(errno.EEXIST, path) self.filesystem.add_object(head, FakeFile( tail, mode & ~self.filesystem.umask, filesystem=self.filesystem)) def symlink(self, src: AnyStr, dst: AnyStr, *, dir_fd: Optional[int] = None) -> None: """Creates the specified symlink, pointed at the specified link target. Args: src: The target of the symlink. dst: Path to the symlink to create. dir_fd: If not `None`, the file descriptor of a directory, with `src` being relative to this directory. Raises: OSError: if the file already exists. """ src = self._path_with_dir_fd(src, self.symlink, dir_fd) self.filesystem.create_symlink( dst, src, create_missing_dirs=False) def link(self, src: AnyStr, dst: AnyStr, *, src_dir_fd: Optional[int] = None, dst_dir_fd: Optional[int] = None) -> None: """Create a hard link at new_path, pointing at old_path. Args: src: An existing path to the target file. dst: The destination path to create a new link at. src_dir_fd: If not `None`, the file descriptor of a directory, with `src` being relative to this directory. dst_dir_fd: If not `None`, the file descriptor of a directory, with `dst` being relative to this directory. Raises: OSError: if something already exists at new_path. OSError: if the parent directory doesn't exist. """ src = self._path_with_dir_fd(src, self.link, src_dir_fd) dst = self._path_with_dir_fd(dst, self.link, dst_dir_fd) self.filesystem.link(src, dst) def fsync(self, fd: int) -> None: """Perform fsync for a fake file (in other words, do nothing). Args: fd: The file descriptor of the open file. Raises: OSError: file_des is an invalid file descriptor. TypeError: file_des is not an integer. """ # Throw an error if file_des isn't valid if 0 <= fd < NR_STD_STREAMS: self.filesystem.raise_os_error(errno.EINVAL) file_object = cast(FakeFileWrapper, self.filesystem.get_open_file(fd)) if self.filesystem.is_windows_fs: if (not hasattr(file_object, 'allow_update') or not file_object.allow_update): self.filesystem.raise_os_error( errno.EBADF, file_object.file_path) def fdatasync(self, fd: int) -> None: """Perform fdatasync for a fake file (in other words, do nothing). Args: fd: The file descriptor of the open file. Raises: OSError: `fd` is an invalid file descriptor. TypeError: `fd` is not an integer. """ if self.filesystem.is_windows_fs or self.filesystem.is_macos: raise AttributeError("module 'os' has no attribute 'fdatasync'") # Throw an error if file_des isn't valid if 0 <= fd < NR_STD_STREAMS: self.filesystem.raise_os_error(errno.EINVAL) self.filesystem.get_open_file(fd) def sendfile(self, fd_out: int, fd_in: int, offset: int, count: int) -> int: """Copy count bytes from file descriptor fd_in to file descriptor fd_out starting at offset. Args: fd_out: The file descriptor of the destination file. fd_in: The file descriptor of the source file. offset: The offset in bytes where to start the copy in the source file. If `None` (Linux only), copying is started at the current position, and the position is updated. count: The number of bytes to copy. If 0, all remaining bytes are copied (MacOs only). Raises: OSError: If `fd_in` or `fd_out` is an invalid file descriptor. TypeError: If `fd_in` or `fd_out` is not an integer. TypeError: If `offset` is None under MacOs. """ if self.filesystem.is_windows_fs: raise AttributeError("module 'os' has no attribute 'sendfile'") if 0 <= fd_in < NR_STD_STREAMS: self.filesystem.raise_os_error(errno.EINVAL) if 0 <= fd_out < NR_STD_STREAMS: self.filesystem.raise_os_error(errno.EINVAL) source = cast(FakeFileWrapper, self.filesystem.get_open_file(fd_in)) dest = cast(FakeFileWrapper, self.filesystem.get_open_file(fd_out)) if self.filesystem.is_macos: if dest.get_object().stat_result.st_mode & 0o777000 != S_IFSOCK: raise OSError('Socket operation on non-socket') if offset is None: if self.filesystem.is_macos: raise TypeError('None is not a valid offset') contents = source.read(count) else: position = source.tell() source.seek(offset) if count == 0 and self.filesystem.is_macos: contents = source.read() else: contents = source.read(count) source.seek(position) if contents: written = dest.write(contents) dest.flush() return written return 0 def __getattr__(self, name: str) -> Any: """Forwards any unfaked calls to the standard os module.""" return getattr(self._os_module, name) class FakeIoModule: """Uses FakeFilesystem to provide a fake io module replacement. Currently only used to wrap `io.open()` which is an alias to `open()`. You need a fake_filesystem to use this: filesystem = fake_filesystem.FakeFilesystem() my_io_module = fake_filesystem.FakeIoModule(filesystem) """ @staticmethod def dir() -> List[str]: """Return the list of patched function names. Used for patching functions imported from the module. """ _dir = ['open'] if sys.version_info >= (3, 8): _dir.append('open_code') return _dir def __init__(self, filesystem: FakeFilesystem): """ Args: filesystem: FakeFilesystem used to provide file system information. """ self.filesystem = filesystem self.skip_names: List[str] = [] self._io_module = io def open(self, file: Union[AnyStr, int], mode: str = 'r', buffering: int = -1, encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None, closefd: bool = True, opener: Optional[Callable] = None) -> Union[AnyFileWrapper, IO[Any]]: """Redirect the call to FakeFileOpen. See FakeFileOpen.call() for description. """ # workaround for built-in open called from skipped modules (see #552) # as open is not imported explicitly, we cannot patch it for # specific modules; instead we check if the caller is a skipped # module (should work in most cases) stack = traceback.extract_stack(limit=2) module_name = os.path.splitext(stack[0].filename)[0] module_name = module_name.replace(os.sep, '.') if any([module_name == sn or module_name.endswith('.' + sn) for sn in self.skip_names]): return io.open(file, mode, buffering, encoding, errors, newline, closefd, opener) fake_open = FakeFileOpen(self.filesystem) return fake_open(file, mode, buffering, encoding, errors, newline, closefd, opener) if sys.version_info >= (3, 8): def open_code(self, path): """Redirect the call to open. Note that the behavior of the real function may be overridden by an earlier call to the PyFile_SetOpenCodeHook(). This behavior is not reproduced here. """ if not isinstance(path, str): raise TypeError( "open_code() argument 'path' must be str, not int") patch_mode = self.filesystem.patch_open_code if (patch_mode == PatchMode.AUTO and self.filesystem.exists(path) or patch_mode == PatchMode.ON): return self.open(path, mode='rb') # mostly this is used for compiled code - # don't patch these, as the files are probably in the real fs return self._io_module.open_code(path) def __getattr__(self, name): """Forwards any unfaked calls to the standard io module.""" return getattr(self._io_module, name) if sys.platform != 'win32': import fcntl class FakeFcntlModule: """Replaces the fcntl module. Only valid under Linux/MacOS, currently just mocks the functionality away. """ @staticmethod def dir() -> List[str]: """Return the list of patched function names. Used for patching functions imported from the module. """ return ['fcntl', 'ioctl', 'flock', 'lockf'] def __init__(self, filesystem: FakeFilesystem): """ Args: filesystem: FakeFilesystem used to provide file system information (currently not used). """ self.filesystem = filesystem self._fcntl_module = fcntl def fcntl(self, fd: int, cmd: int, arg: int = 0) -> Union[int, bytes]: return 0 def ioctl(self, fd: int, request: int, arg: int = 0, mutate_flag: bool = True) -> Union[int, bytes]: return 0 def flock(self, fd: int, operation: int) -> None: pass def lockf(self, fd: int, cmd: int, len: int = 0, start: int = 0, whence=0) -> Any: pass def __getattr__(self, name): """Forwards any unfaked calls to the standard fcntl module.""" return getattr(self._fcntl_module, name) class FakeFileWrapper: """Wrapper for a stream object for use by a FakeFile object. If the wrapper has any data written to it, it will propagate to the FakeFile object on close() or flush(). """ def __init__(self, file_object: FakeFile, file_path: AnyStr, update: bool, read: bool, append: bool, delete_on_close: bool, filesystem: FakeFilesystem, newline: Optional[str], binary: bool, closefd: bool, encoding: Optional[str], errors: Optional[str], buffering: int, raw_io: bool, is_stream: bool = False): self.file_object = file_object self.file_path = file_path # type: ignore[var-annotated] self._append = append self._read = read self.allow_update = update self._closefd = closefd self._file_epoch = file_object.epoch self.raw_io = raw_io self._binary = binary self.is_stream = is_stream self._changed = False self._buffer_size = buffering if self._buffer_size == 0 and not binary: raise ValueError("can't have unbuffered text I/O") # buffer_size is ignored in text mode elif self._buffer_size == -1 or not binary: self._buffer_size = io.DEFAULT_BUFFER_SIZE self._use_line_buffer = not binary and buffering == 1 contents = file_object.byte_contents self._encoding = encoding or locale.getpreferredencoding(False) errors = errors or 'strict' self._io: Union[BinaryBufferIO, TextBufferIO] = ( BinaryBufferIO(contents) if binary else TextBufferIO(contents, encoding=encoding, newline=newline, errors=errors) ) self._read_whence = 0 self._read_seek = 0 self._flush_pos = 0 if contents: self._flush_pos = len(contents) if update: if not append: self._io.seek(0) else: self._io.seek(self._flush_pos) self._read_seek = self._io.tell() if delete_on_close: assert filesystem, 'delete_on_close=True requires filesystem' self._filesystem = filesystem self.delete_on_close = delete_on_close # override, don't modify FakeFile.name, as FakeFilesystem expects # it to be the file name only, no directories. self.name = file_object.opened_as self.filedes: Optional[int] = None def __enter__(self) -> 'FakeFileWrapper': """To support usage of this fake file with the 'with' statement.""" return self def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType] ) -> None: """To support usage of this fake file with the 'with' statement.""" self.close() def _raise(self, message: str) -> NoReturn: if self.raw_io: self._filesystem.raise_os_error(errno.EBADF, self.file_path) raise io.UnsupportedOperation(message) def get_object(self) -> FakeFile: """Return the FakeFile object that is wrapped by the current instance. """ return self.file_object def fileno(self) -> int: """Return the file descriptor of the file object.""" if self.filedes is not None: return self.filedes raise OSError(errno.EBADF, 'Invalid file descriptor') def close(self) -> None: """Close the file.""" # ignore closing a closed file if not self._is_open(): return # for raw io, all writes are flushed immediately if self.allow_update and not self.raw_io: self.flush() if self._filesystem.is_windows_fs and self._changed: self.file_object.st_mtime = now() assert self.filedes is not None if self._closefd: self._filesystem._close_open_file(self.filedes) else: open_files = self._filesystem.open_files[self.filedes] assert open_files is not None open_files.remove(self) if self.delete_on_close: self._filesystem.remove_object( self.get_object().path) # type: ignore[arg-type] @property def closed(self) -> bool: """Simulate the `closed` attribute on file.""" return not self._is_open() def _try_flush(self, old_pos: int) -> None: """Try to flush and reset the position if it fails.""" flush_pos = self._flush_pos try: self.flush() except OSError: # write failed - reset to previous position self._io.seek(old_pos) self._io.truncate() self._flush_pos = flush_pos raise def flush(self) -> None: """Flush file contents to 'disk'.""" self._check_open_file() if self.allow_update and not self.is_stream: contents = self._io.getvalue() if self._append: self._sync_io() old_contents = self.file_object.byte_contents assert old_contents is not None contents = old_contents + contents[self._flush_pos:] self._set_stream_contents(contents) else: self._io.flush() self.update_flush_pos() if self.file_object.set_contents(contents, self._encoding): if self._filesystem.is_windows_fs: self._changed = True else: current_time = now() self.file_object.st_ctime = current_time self.file_object.st_mtime = current_time self._file_epoch = self.file_object.epoch if not self.is_stream: self._flush_related_files() def update_flush_pos(self) -> None: self._flush_pos = self._io.tell() def _flush_related_files(self) -> None: for open_files in self._filesystem.open_files[3:]: if open_files is not None: for open_file in open_files: if (open_file is not self and isinstance(open_file, FakeFileWrapper) and self.file_object == open_file.file_object and not open_file._append): open_file._sync_io() def seek(self, offset: int, whence: int = 0) -> None: """Move read/write pointer in 'file'.""" self._check_open_file() if not self._append: self._io.seek(offset, whence) else: self._read_seek = offset self._read_whence = whence if not self.is_stream: self.flush() def tell(self) -> int: """Return the file's current position. Returns: int, file's current position in bytes. """ self._check_open_file() if not self.is_stream: self.flush() if not self._append: return self._io.tell() if self._read_whence: write_seek = self._io.tell() self._io.seek(self._read_seek, self._read_whence) self._read_seek = self._io.tell() self._read_whence = 0 self._io.seek(write_seek) return self._read_seek def _sync_io(self) -> None: """Update the stream with changes to the file object contents.""" if self._file_epoch == self.file_object.epoch: return contents = self.file_object.byte_contents assert contents is not None self._set_stream_contents(contents) self._file_epoch = self.file_object.epoch def _set_stream_contents(self, contents: bytes) -> None: whence = self._io.tell() self._io.seek(0) self._io.truncate() self._io.putvalue(contents) if not self._append: self._io.seek(whence) def _read_wrappers(self, name: str) -> Callable: """Wrap a stream attribute in a read wrapper. Returns a read_wrapper which tracks our own read pointer since the stream object has no concept of a different read and write pointer. Args: name: The name of the attribute to wrap. Should be a read call. Returns: The read_wrapper function. """ io_attr = getattr(self._io, name) def read_wrapper(*args, **kwargs): """Wrap all read calls to the stream object. We do this to track the read pointer separate from the write pointer. Anything that wants to read from the stream object while we're in append mode goes through this. Args: *args: pass through args **kwargs: pass through kwargs Returns: Wrapped stream object method """ self._io.seek(self._read_seek, self._read_whence) ret_value = io_attr(*args, **kwargs) self._read_seek = self._io.tell() self._read_whence = 0 self._io.seek(0, 2) return ret_value return read_wrapper def _other_wrapper(self, name: str) -> Callable: """Wrap a stream attribute in an other_wrapper. Args: name: the name of the stream attribute to wrap. Returns: other_wrapper which is described below. """ io_attr = getattr(self._io, name) def other_wrapper(*args, **kwargs): """Wrap all other calls to the stream Object. We do this to track changes to the write pointer. Anything that moves the write pointer in a file open for appending should move the read pointer as well. Args: *args: Pass through args. **kwargs: Pass through kwargs. Returns: Wrapped stream object method. """ write_seek = self._io.tell() ret_value = io_attr(*args, **kwargs) if write_seek != self._io.tell(): self._read_seek = self._io.tell() self._read_whence = 0 return ret_value return other_wrapper def _write_wrapper(self, name: str) -> Callable: """Wrap a stream attribute in a write_wrapper. Args: name: the name of the stream attribute to wrap. Returns: write_wrapper which is described below. """ io_attr = getattr(self._io, name) def write_wrapper(*args, **kwargs): """Wrap all other calls to the stream Object. We do this to track changes to the write pointer. Anything that moves the write pointer in a file open for appending should move the read pointer as well. Args: *args: Pass through args. **kwargs: Pass through kwargs. Returns: Wrapped stream object method. """ old_pos = self._io.tell() ret_value = io_attr(*args, **kwargs) new_pos = self._io.tell() # if the buffer size is exceeded, we flush use_line_buf = self._use_line_buffer and '\n' in args[0] if new_pos - self._flush_pos > self._buffer_size or use_line_buf: flush_all = (new_pos - old_pos > self._buffer_size or use_line_buf) # if the current write does not exceed the buffer size, # we revert to the previous position and flush that, # otherwise we flush all if not flush_all: self._io.seek(old_pos) self._io.truncate() self._try_flush(old_pos) if not flush_all: ret_value = io_attr(*args, **kwargs) if self._append: self._read_seek = self._io.tell() self._read_whence = 0 return ret_value return write_wrapper def _adapt_size_for_related_files(self, size: int) -> None: for open_files in self._filesystem.open_files[3:]: if open_files is not None: for open_file in open_files: if (open_file is not self and isinstance(open_file, FakeFileWrapper) and self.file_object == open_file.file_object and cast(FakeFileWrapper, open_file)._append): open_file._read_seek += size def _truncate_wrapper(self) -> Callable: """Wrap truncate() to allow flush after truncate. Returns: Wrapper which is described below. """ io_attr = getattr(self._io, 'truncate') def truncate_wrapper(*args, **kwargs): """Wrap truncate call to call flush after truncate.""" if self._append: self._io.seek(self._read_seek, self._read_whence) size = io_attr(*args, **kwargs) self.flush() if not self.is_stream: self.file_object.size = size buffer_size = len(self._io.getvalue()) if buffer_size < size: self._io.seek(buffer_size) self._io.putvalue(b'\0' * (size - buffer_size)) self.file_object.set_contents( self._io.getvalue(), self._encoding) self._flush_pos = size self._adapt_size_for_related_files(size - buffer_size) self.flush() return size return truncate_wrapper def size(self) -> int: """Return the content size in bytes of the wrapped file.""" return self.file_object.st_size def __getattr__(self, name: str) -> Any: if self.file_object.is_large_file(): raise FakeLargeFileIoException(self.file_path) reading = name.startswith('read') or name == 'next' truncate = name == 'truncate' writing = name.startswith('write') or truncate if reading or writing: self._check_open_file() if not self._read and reading: return self._read_error() if not self.allow_update and writing: return self._write_error() if reading: self._sync_io() if not self.is_stream: self.flush() if not self._filesystem.is_windows_fs: self.file_object.st_atime = now() if truncate: return self._truncate_wrapper() if self._append: if reading: return self._read_wrappers(name) elif not writing: return self._other_wrapper(name) if writing: return self._write_wrapper(name) return getattr(self._io, name) def _read_error(self) -> Callable: def read_error(*args, **kwargs): """Throw an error unless the argument is zero.""" if args and args[0] == 0: if self._filesystem.is_windows_fs and self.raw_io: return b'' if self._binary else u'' self._raise('File is not open for reading.') return read_error def _write_error(self) -> Callable: def write_error(*args, **kwargs): """Throw an error.""" if self.raw_io: if (self._filesystem.is_windows_fs and args and len(args[0]) == 0): return 0 self._raise('File is not open for writing.') return write_error def _is_open(self) -> bool: if (self.filedes is not None and self.filedes < len(self._filesystem.open_files)): open_files = self._filesystem.open_files[self.filedes] if open_files is not None and self in open_files: return True return False def _check_open_file(self) -> None: if not self.is_stream and not self._is_open(): raise ValueError('I/O operation on closed file') def __iter__(self) -> Union[Iterator[str], Iterator[bytes]]: if not self._read: self._raise('File is not open for reading') return self._io.__iter__() def __next__(self): if not self._read: self._raise('File is not open for reading') return next(self._io) class StandardStreamWrapper: """Wrapper for a system standard stream to be used in open files list. """ def __init__(self, stream_object: TextIO): self._stream_object = stream_object self.filedes: Optional[int] = None def get_object(self) -> TextIO: return self._stream_object def fileno(self) -> int: """Return the file descriptor of the wrapped standard stream.""" if self.filedes is not None: return self.filedes raise OSError(errno.EBADF, 'Invalid file descriptor') def read(self, n: int) -> bytes: return cast(bytes, self._stream_object.read()) def close(self) -> None: """We do not support closing standard streams.""" pass def is_stream(self) -> bool: return True class FakeDirWrapper: """Wrapper for a FakeDirectory object to be used in open files list. """ def __init__(self, file_object: FakeDirectory, file_path: AnyString, filesystem: FakeFilesystem): self.file_object = file_object self.file_path = file_path self._filesystem = filesystem self.filedes: Optional[int] = None def get_object(self) -> FakeDirectory: """Return the FakeFile object that is wrapped by the current instance. """ return self.file_object def fileno(self) -> int: """Return the file descriptor of the file object.""" if self.filedes is not None: return self.filedes raise OSError(errno.EBADF, 'Invalid file descriptor') def close(self) -> None: """Close the directory.""" assert self.filedes is not None self._filesystem._close_open_file(self.filedes) class FakePipeWrapper: """Wrapper for a read or write descriptor of a real pipe object to be used in open files list. """ def __init__(self, filesystem: FakeFilesystem, fd: int, can_write: bool): self._filesystem = filesystem self.fd = fd # the real file descriptor self.can_write = can_write self.file_object = None self.filedes: Optional[int] = None def __enter__(self) -> 'FakePipeWrapper': """To support usage of this fake pipe with the 'with' statement.""" return self def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType] ) -> None: """To support usage of this fake pipe with the 'with' statement.""" self.close() def get_object(self) -> None: return self.file_object def fileno(self) -> int: """Return the fake file descriptor of the pipe object.""" if self.filedes is not None: return self.filedes raise OSError(errno.EBADF, 'Invalid file descriptor') def read(self, numBytes: int) -> bytes: """Read from the real pipe.""" return os.read(self.fd, numBytes) def flush(self) -> None: """Flush the real pipe?""" pass def write(self, contents: bytes) -> int: """Write to the real pipe.""" return os.write(self.fd, contents) def close(self) -> None: """Close the pipe descriptor.""" assert self.filedes is not None open_files = self._filesystem.open_files[self.filedes] assert open_files is not None open_files.remove(self) os.close(self.fd) def readable(self) -> bool: """The pipe end can either be readable or writable.""" return not self.can_write def writable(self) -> bool: """The pipe end can either be readable or writable.""" return self.can_write def seekable(self) -> bool: """A pipe is not seekable.""" return False Deprecator.add(FakeFileWrapper, FakeFileWrapper.get_object, 'GetObject') Deprecator.add(FakeFileWrapper, FakeFileWrapper.size, 'Size') class FakeFileOpen: """Faked `file()` and `open()` function replacements. Returns FakeFile objects in a FakeFilesystem in place of the `file()` or `open()` function. """ __name__ = 'FakeFileOpen' def __init__(self, filesystem: FakeFilesystem, delete_on_close: bool = False, raw_io: bool = False): """ Args: filesystem: FakeFilesystem used to provide file system information delete_on_close: optional boolean, deletes file on close() """ self.filesystem = filesystem self._delete_on_close = delete_on_close self.raw_io = raw_io def __call__(self, *args: Any, **kwargs: Any) -> AnyFileWrapper: """Redirects calls to file() or open() to appropriate method.""" return self.call(*args, **kwargs) def call(self, file_: Union[AnyStr, int], mode: str = 'r', buffering: int = -1, encoding: Optional[str] = None, errors: Optional[str] = None, newline: Optional[str] = None, closefd: bool = True, opener: Any = None, open_modes: Optional[_OpenModes] = None) -> AnyFileWrapper: """Return a file-like object with the contents of the target file object. Args: file_: Path to target file or a file descriptor. mode: Additional file modes (all modes in `open()` are supported). buffering: the buffer size used for writing. Data will only be flushed if buffer size is exceeded. The default (-1) uses a system specific default buffer size. Text line mode (e.g. buffering=1 in text mode) is not supported. encoding: The encoding used to encode unicode strings / decode bytes. errors: (str) Defines how encoding errors are handled. newline: Controls universal newlines, passed to stream object. closefd: If a file descriptor rather than file name is passed, and this is set to `False`, then the file descriptor is kept open when file is closed. opener: not supported. open_modes: Modes for opening files if called from low-level API. Returns: A file-like object containing the contents of the target file. Raises: OSError depending on Python version / call mode: - if the target object is a directory - on an invalid path - if the file does not exist when it should - if the file exists but should not - if permission is denied ValueError: for an invalid mode or mode combination """ binary = 'b' in mode newline, open_modes = self._handle_file_mode(mode, newline, open_modes) file_object, file_path, filedes, real_path = self._handle_file_arg( file_) if file_object is None and file_path is None: # file must be a fake pipe wrapper, find it... if (filedes is None or len(self.filesystem.open_files) <= filedes or not self.filesystem.open_files[filedes]): raise OSError(errno.EBADF, 'invalid pipe file descriptor') wrappers = self.filesystem.open_files[filedes] assert wrappers is not None existing_wrapper = wrappers[0] assert isinstance(existing_wrapper, FakePipeWrapper) wrapper = FakePipeWrapper(self.filesystem, existing_wrapper.fd, existing_wrapper.can_write) file_des = self.filesystem._add_open_file(wrapper) wrapper.filedes = file_des return wrapper assert file_path is not None if not filedes: closefd = True if (open_modes.must_not_exist and (file_object or self.filesystem.islink(file_path) and not self.filesystem.is_windows_fs)): self.filesystem.raise_os_error(errno.EEXIST, file_path) assert real_path is not None file_object = self._init_file_object(file_object, file_path, open_modes, real_path) if S_ISDIR(file_object.st_mode): if self.filesystem.is_windows_fs: self.filesystem.raise_os_error(errno.EACCES, file_path) else: self.filesystem.raise_os_error(errno.EISDIR, file_path) # If you print obj.name, the argument to open() must be printed. # Not the abspath, not the filename, but the actual argument. file_object.opened_as = file_path if open_modes.truncate: current_time = now() file_object.st_mtime = current_time if not self.filesystem.is_windows_fs: file_object.st_ctime = current_time fakefile = FakeFileWrapper(file_object, file_path, update=open_modes.can_write, read=open_modes.can_read, append=open_modes.append, delete_on_close=self._delete_on_close, filesystem=self.filesystem, newline=newline, binary=binary, closefd=closefd, encoding=encoding, errors=errors, buffering=buffering, raw_io=self.raw_io) if filedes is not None: fakefile.filedes = filedes # replace the file wrapper open_files_list = self.filesystem.open_files[filedes] assert open_files_list is not None open_files_list.append(fakefile) else: fakefile.filedes = self.filesystem._add_open_file(fakefile) return fakefile def _init_file_object(self, file_object: Optional[FakeFile], file_path: AnyStr, open_modes: _OpenModes, real_path: AnyString) -> FakeFile: if file_object: if (not is_root() and ((open_modes.can_read and not file_object.st_mode & PERM_READ) or (open_modes.can_write and not file_object.st_mode & PERM_WRITE))): self.filesystem.raise_os_error(errno.EACCES, file_path) if open_modes.can_write: if open_modes.truncate: file_object.set_contents('') else: if open_modes.must_exist: self.filesystem.raise_os_error(errno.ENOENT, file_path) if self.filesystem.islink(file_path): link_object = self.filesystem.resolve(file_path, follow_symlinks=False) assert link_object.contents is not None target_path = cast(AnyStr, link_object.contents) else: target_path = file_path if self.filesystem.ends_with_path_separator(target_path): error = ( errno.EINVAL if self.filesystem.is_windows_fs else errno.ENOENT if self.filesystem.is_macos else errno.EISDIR ) self.filesystem.raise_os_error(error, file_path) file_object = self.filesystem.create_file_internally( real_path, create_missing_dirs=False, apply_umask=True) return file_object def _handle_file_arg(self, file_: Union[AnyStr, int]) -> Tuple[ Optional[FakeFile], Optional[AnyStr], Optional[int], Optional[AnyStr]]: file_object = None if isinstance(file_, int): # opening a file descriptor filedes: int = file_ wrapper = self.filesystem.get_open_file(filedes) if isinstance(wrapper, FakePipeWrapper): return None, None, filedes, None if isinstance(wrapper, FakeFileWrapper): self._delete_on_close = wrapper.delete_on_close file_object = cast(FakeFile, self.filesystem.get_open_file( filedes).get_object()) assert file_object is not None path = file_object.name return file_object, cast(AnyStr, path), filedes, cast(AnyStr, path) # open a file file by path file_path = cast(AnyStr, file_) if file_path == self.filesystem.dev_null.name: file_object = self.filesystem.dev_null real_path = file_path else: real_path = self.filesystem.resolve_path(file_path) if self.filesystem.exists(file_path): file_object = self.filesystem.get_object_from_normpath( real_path, check_read_perm=False) return file_object, file_path, None, real_path def _handle_file_mode( self, mode: str, newline: Optional[str], open_modes: Optional[_OpenModes]) -> Tuple[Optional[str], _OpenModes]: orig_modes = mode # Save original modes for error messages. # Normalize modes. Handle 't' and 'U'. if 'b' in mode and 't' in mode: raise ValueError('Invalid mode: ' + mode) mode = mode.replace('t', '').replace('b', '') mode = mode.replace('rU', 'r').replace('U', 'r') if not self.raw_io: if mode not in _OPEN_MODE_MAP: raise ValueError('Invalid mode: %r' % orig_modes) open_modes = _OpenModes(*_OPEN_MODE_MAP[mode]) assert open_modes is not None return newline, open_modes def _run_doctest() -> TestResults: import doctest import pyfakefs return doctest.testmod(pyfakefs.fake_filesystem) if __name__ == '__main__': _run_doctest() pyfakefs-4.5.4/pyfakefs/fake_filesystem_shutil.py0000666000000000000000000000427613757263357020445 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A fake shutil module implementation that uses fake_filesystem for unit tests. Note that only `shutildisk_usage()` is faked, the rest of the functions shall work fine with the fake file system if `os`/`os.path` are patched. :Includes: FakeShutil: Uses a FakeFilesystem to provide a fake replacement for the shutil module. :Usage: The fake implementation is automatically involved if using `fake_filesystem_unittest.TestCase`, pytest fs fixture, or directly `Patcher`. """ import shutil class FakeShutilModule: """Uses a FakeFilesystem to provide a fake replacement for shutil module. """ @staticmethod def dir(): """Return the list of patched function names. Used for patching functions imported from the module. """ return 'disk_usage', def __init__(self, filesystem): """Construct fake shutil module using the fake filesystem. Args: filesystem: FakeFilesystem used to provide file system information """ self.filesystem = filesystem self._shutil_module = shutil def disk_usage(self, path): """Return the total, used and free disk space in bytes as named tuple or placeholder holder values simulating unlimited space if not set. Args: path: defines the filesystem device which is queried """ return self.filesystem.get_disk_usage(path) def __getattr__(self, name): """Forwards any non-faked calls to the standard shutil module.""" return getattr(self._shutil_module, name) pyfakefs-4.5.4/pyfakefs/fake_filesystem_unittest.py0000666000000000000000000012100714147521400020760 0ustar 00000000000000# Copyright 2014 Altera Corporation. All Rights Reserved. # Copyright 2015-2017 John McGehee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """This module provides a base class derived from `unittest.TestClass` for unit tests using the :py:class:`pyfakefs` module. `fake_filesystem_unittest.TestCase` searches `sys.modules` for modules that import the `os`, `io`, `path` `shutil`, and `pathlib` modules. The `setUpPyfakefs()` method binds these modules to the corresponding fake modules from `pyfakefs`. Further, the `open()` built-in is bound to a fake `open()`. It is expected that `setUpPyfakefs()` be invoked at the beginning of the derived class' `setUp()` method. There is no need to add anything to the derived class' `tearDown()` method. During the test, everything uses the fake file system and modules. This means that even in your test fixture, familiar functions like `open()` and `os.makedirs()` manipulate the fake file system. Existing unit tests that use the real file system can be retrofitted to use pyfakefs by simply changing their base class from `:py:class`unittest.TestCase` to `:py:class`pyfakefs.fake_filesystem_unittest.TestCase`. """ import doctest import functools import inspect import linecache import shutil import sys import tempfile import tokenize from importlib.abc import Loader, MetaPathFinder from types import ModuleType, TracebackType, FunctionType from typing import ( Any, Callable, Dict, List, Set, Tuple, Optional, Union, AnyStr, Type, Iterator, cast, ItemsView, Sequence ) import unittest import warnings from unittest import TestSuite from pyfakefs.deprecator import Deprecator from pyfakefs.fake_filesystem import ( set_uid, set_gid, reset_ids, PatchMode, FakeFile, FakeFilesystem ) from pyfakefs.helpers import IS_PYPY from pyfakefs.mox3_stubout import StubOutForTesting try: from importlib.machinery import ModuleSpec except ImportError: ModuleSpec = object # type: ignore[assignment, misc] from importlib import reload from pyfakefs import fake_filesystem from pyfakefs import fake_filesystem_shutil from pyfakefs import fake_pathlib from pyfakefs import mox3_stubout from pyfakefs.extra_packages import pathlib2, use_scandir if use_scandir: from pyfakefs import fake_scandir OS_MODULE = 'nt' if sys.platform == 'win32' else 'posix' PATH_MODULE = 'ntpath' if sys.platform == 'win32' else 'posixpath' def patchfs(_func: Callable = None, *, additional_skip_names: Optional[ List[Union[str, ModuleType]]] = None, modules_to_reload: Optional[List[ModuleType]] = None, modules_to_patch: Optional[Dict[str, ModuleType]] = None, allow_root_user: bool = True, use_known_patches: bool = True, patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False, use_cache: bool = True) -> Callable: """Convenience decorator to use patcher with additional parameters in a test function. Usage:: @patchfs def test_my_function(fake_fs): fake_fs.create_file('foo') @patchfs(allow_root_user=False) def test_with_patcher_args(fs): os.makedirs('foo/bar') """ def wrap_patchfs(f: Callable) -> Callable: @functools.wraps(f) def wrapped(*args, **kwargs): with Patcher( additional_skip_names=additional_skip_names, modules_to_reload=modules_to_reload, modules_to_patch=modules_to_patch, allow_root_user=allow_root_user, use_known_patches=use_known_patches, patch_open_code=patch_open_code, patch_default_args=patch_default_args, use_cache=use_cache) as p: args = list(args) args.append(p.fs) return f(*args, **kwargs) return wrapped if _func: if not callable(_func): raise TypeError( "Decorator argument is not a function.\n" "Did you mean `@patchfs(additional_skip_names=...)`?" ) if hasattr(_func, 'patchings'): _func.nr_patches = len(_func.patchings) # type: ignore return wrap_patchfs(_func) return wrap_patchfs def load_doctests( loader: Any, tests: TestSuite, ignore: Any, module: ModuleType, additional_skip_names: Optional[ List[Union[str, ModuleType]]] = None, modules_to_reload: Optional[List[ModuleType]] = None, modules_to_patch: Optional[Dict[str, ModuleType]] = None, allow_root_user: bool = True, use_known_patches: bool = True, patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False ) -> TestSuite: # pylint:disable=unused-argument """Load the doctest tests for the specified module into unittest. Args: loader, tests, ignore : arguments passed in from `load_tests()` module: module under test remaining args: see :py:class:`TestCase` for an explanation File `example_test.py` in the pyfakefs release provides a usage example. """ _patcher = Patcher(additional_skip_names=additional_skip_names, modules_to_reload=modules_to_reload, modules_to_patch=modules_to_patch, allow_root_user=allow_root_user, use_known_patches=use_known_patches, patch_open_code=patch_open_code, patch_default_args=patch_default_args) globs = _patcher.replace_globs(vars(module)) tests.addTests(doctest.DocTestSuite(module, globs=globs, setUp=_patcher.setUp, tearDown=_patcher.tearDown)) return tests class TestCaseMixin: """Test case mixin that automatically replaces file-system related modules by fake implementations. Attributes: additional_skip_names: names of modules inside of which no module replacement shall be performed, in addition to the names in :py:attr:`fake_filesystem_unittest.Patcher.SKIPNAMES`. Instead of the module names, the modules themselves may be used. modules_to_reload: A list of modules that need to be reloaded to be patched dynamically; may be needed if the module imports file system modules under an alias .. caution:: Reloading modules may have unwanted side effects. modules_to_patch: A dictionary of fake modules mapped to the fully qualified patched module names. Can be used to add patching of modules not provided by `pyfakefs`. If you specify some of these attributes here and you have DocTests, consider also specifying the same arguments to :py:func:`load_doctests`. Example usage in derived test classes:: from unittest import TestCase from fake_filesystem_unittest import TestCaseMixin class MyTestCase(TestCase, TestCaseMixin): def __init__(self, methodName='runTest'): super(MyTestCase, self).__init__( methodName=methodName, additional_skip_names=['posixpath']) import sut class AnotherTestCase(TestCase, TestCaseMixin): def __init__(self, methodName='runTest'): super(MyTestCase, self).__init__( methodName=methodName, modules_to_reload=[sut]) """ additional_skip_names: Optional[List[Union[str, ModuleType]]] = None modules_to_reload: Optional[List[ModuleType]] = None modules_to_patch: Optional[Dict[str, ModuleType]] = None @property def fs(self) -> FakeFilesystem: return cast(FakeFilesystem, self._stubber.fs) def setUpPyfakefs(self, additional_skip_names: Optional[ List[Union[str, ModuleType]]] = None, modules_to_reload: Optional[List[ModuleType]] = None, modules_to_patch: Optional[Dict[str, ModuleType]] = None, allow_root_user: bool = True, use_known_patches: bool = True, patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False, use_cache: bool = True) -> None: """Bind the file-related modules to the :py:class:`pyfakefs` fake file system instead of the real file system. Also bind the fake `open()` function. Invoke this at the beginning of the `setUp()` method in your unit test class. For the arguments, see the `TestCaseMixin` attribute description. If any of the arguments is not None, it overwrites the settings for the current test case. Settings the arguments here may be a more convenient way to adapt the setting than overwriting `__init__()`. """ if additional_skip_names is None: additional_skip_names = self.additional_skip_names if modules_to_reload is None: modules_to_reload = self.modules_to_reload if modules_to_patch is None: modules_to_patch = self.modules_to_patch self._stubber = Patcher( additional_skip_names=additional_skip_names, modules_to_reload=modules_to_reload, modules_to_patch=modules_to_patch, allow_root_user=allow_root_user, use_known_patches=use_known_patches, patch_open_code=patch_open_code, patch_default_args=patch_default_args, use_cache=use_cache ) self._stubber.setUp() cast(TestCase, self).addCleanup(self._stubber.tearDown) def pause(self) -> None: """Pause the patching of the file system modules until `resume` is called. After that call, all file system calls are executed in the real file system. Calling pause() twice is silently ignored. """ self._stubber.pause() def resume(self) -> None: """Resume the patching of the file system modules if `pause` has been called before. After that call, all file system calls are executed in the fake file system. Does nothing if patching is not paused. """ self._stubber.resume() class TestCase(unittest.TestCase, TestCaseMixin): """Test case class that automatically replaces file-system related modules by fake implementations. Inherits :py:class:`TestCaseMixin`. The arguments are explained in :py:class:`TestCaseMixin`. """ def __init__(self, methodName: str = 'runTest', additional_skip_names: Optional[ List[Union[str, ModuleType]]] = None, modules_to_reload: Optional[List[ModuleType]] = None, modules_to_patch: Optional[Dict[str, ModuleType]] = None): """Creates the test class instance and the patcher used to stub out file system related modules. Args: methodName: The name of the test method (same as in unittest.TestCase) """ super().__init__(methodName) self.additional_skip_names = additional_skip_names self.modules_to_reload = modules_to_reload self.modules_to_patch = modules_to_patch @Deprecator('add_real_file') def copyRealFile(self, real_file_path: AnyStr, fake_file_path: Optional[AnyStr] = None, create_missing_dirs: bool = True) -> FakeFile: """Add the file `real_file_path` in the real file system to the same path in the fake file system. **This method is deprecated** in favor of :py:meth:`FakeFilesystem..add_real_file`. `copyRealFile()` is retained with limited functionality for backward compatibility only. Args: real_file_path: Path to the file in both the real and fake file systems fake_file_path: Deprecated. Use the default, which is `real_file_path`. If a value other than `real_file_path` is specified, a `ValueError` exception will be raised. create_missing_dirs: Deprecated. Use the default, which creates missing directories in the fake file system. If `False` is specified, a `ValueError` exception is raised. Returns: The newly created FakeFile object. Raises: OSError: If the file already exists in the fake file system. ValueError: If deprecated argument values are specified. See: :py:meth:`FakeFileSystem.add_real_file` """ if fake_file_path is not None and real_file_path != fake_file_path: raise ValueError("CopyRealFile() is deprecated and no longer " "supports different real and fake file paths") if not create_missing_dirs: raise ValueError("CopyRealFile() is deprecated and no longer " "supports NOT creating missing directories") assert self._stubber.fs is not None return self._stubber.fs.add_real_file(real_file_path, read_only=False) def tearDownPyfakefs(self) -> None: """This method is deprecated and exists only for backward compatibility. It does nothing. """ pass class Patcher: """ Instantiate a stub creator to bind and un-bind the file-related modules to the :py:mod:`pyfakefs` fake modules. The arguments are explained in :py:class:`TestCaseMixin`. :py:class:`Patcher` is used in :py:class:`TestCaseMixin`. :py:class:`Patcher` also works as a context manager for other tests:: with Patcher(): doStuff() """ '''Stub nothing that is imported within these modules. `sys` is included to prevent `sys.path` from being stubbed with the fake `os.path`. The `pytest` and `py` modules are used by pytest and have to access the real file system. The `linecache` module is used to read the test file in case of test failure to get traceback information before test tear down. In order to make sure that reading the test file is not faked, we skip faking the module. We also have to set back the cached open function in tokenize. ''' SKIPMODULES = { None, fake_filesystem, fake_filesystem_shutil, sys, linecache, tokenize } # caches all modules that do not have file system modules or function # to speed up _find_modules CACHED_MODULES: Set[ModuleType] = set() FS_MODULES: Dict[str, Set[Tuple[ModuleType, str]]] = {} FS_FUNCTIONS: Dict[Tuple[str, str, str], Set[ModuleType]] = {} FS_DEFARGS: List[Tuple[FunctionType, int, Callable[..., Any]]] = [] SKIPPED_FS_MODULES: Dict[str, Set[Tuple[ModuleType, str]]] = {} assert None in SKIPMODULES, ("sys.modules contains 'None' values;" " must skip them.") IS_WINDOWS = sys.platform in ('win32', 'cygwin') SKIPNAMES = {'os', 'path', 'io', 'genericpath', 'fcntl', OS_MODULE, PATH_MODULE} # hold values from last call - if changed, the cache has to be invalidated PATCHED_MODULE_NAMES: Set[str] = set() ADDITIONAL_SKIP_NAMES: Set[str] = set() PATCH_DEFAULT_ARGS = False def __init__(self, additional_skip_names: Optional[ List[Union[str, ModuleType]]] = None, modules_to_reload: Optional[List[ModuleType]] = None, modules_to_patch: Optional[Dict[str, ModuleType]] = None, allow_root_user: bool = True, use_known_patches: bool = True, patch_open_code: PatchMode = PatchMode.OFF, patch_default_args: bool = False, use_cache: bool = True) -> None: """ Args: additional_skip_names: names of modules inside of which no module replacement shall be performed, in addition to the names in :py:attr:`fake_filesystem_unittest.Patcher.SKIPNAMES`. Instead of the module names, the modules themselves may be used. modules_to_reload: A list of modules that need to be reloaded to be patched dynamically; may be needed if the module imports file system modules under an alias .. caution:: Reloading modules may have unwanted side effects. modules_to_patch: A dictionary of fake modules mapped to the fully qualified patched module names. Can be used to add patching of modules not provided by `pyfakefs`. allow_root_user: If True (default), if the test is run as root user, the user in the fake file system is also considered a root user, otherwise it is always considered a regular user. use_known_patches: If True (the default), some patches for commonly used packages are applied which make them usable with pyfakefs. patch_open_code: If True, `io.open_code` is patched. The default is not to patch it, as it mostly is used to load compiled modules that are not in the fake file system. patch_default_args: If True, default arguments are checked for file system functions, which are patched. This check is expansive, so it is off by default. use_cache: If True (default), patched and non-patched modules are cached between tests for performance reasons. As this is a new feature, this argument allows to turn it off in case it causes any problems. """ if not allow_root_user: # set non-root IDs even if the real user is root set_uid(1) set_gid(1) self._skip_names = self.SKIPNAMES.copy() # save the original open function for use in pytest plugin self.original_open = open self.patch_open_code = patch_open_code if additional_skip_names is not None: skip_names = [ cast(ModuleType, m).__name__ if inspect.ismodule(m) else cast(str, m) for m in additional_skip_names ] self._skip_names.update(skip_names) self._fake_module_classes: Dict[str, Any] = {} self._unfaked_module_classes: Dict[str, Any] = {} self._class_modules: Dict[str, List[str]] = {} self._init_fake_module_classes() # reload tempfile under posix to patch default argument self.modules_to_reload: List[ModuleType] = ( [] if sys.platform == 'win32' else [tempfile] ) if modules_to_reload is not None: self.modules_to_reload.extend(modules_to_reload) self.patch_default_args = patch_default_args self.use_cache = use_cache if use_known_patches: from pyfakefs.patched_packages import ( get_modules_to_patch, get_classes_to_patch, get_fake_module_classes ) modules_to_patch = modules_to_patch or {} modules_to_patch.update(get_modules_to_patch()) self._class_modules.update(get_classes_to_patch()) self._fake_module_classes.update(get_fake_module_classes()) if modules_to_patch is not None: for name, fake_module in modules_to_patch.items(): self._fake_module_classes[name] = fake_module patched_module_names = set(modules_to_patch) else: patched_module_names = set() clear_cache = not use_cache if use_cache: if patched_module_names != self.PATCHED_MODULE_NAMES: self.__class__.PATCHED_MODULE_NAMES = patched_module_names clear_cache = True if self._skip_names != self.ADDITIONAL_SKIP_NAMES: self.__class__.ADDITIONAL_SKIP_NAMES = self._skip_names clear_cache = True if patch_default_args != self.PATCH_DEFAULT_ARGS: self.__class__.PATCH_DEFAULT_ARGS = patch_default_args clear_cache = True if clear_cache: self.clear_cache() self._fake_module_functions: Dict[str, Dict] = {} self._init_fake_module_functions() # Attributes set by _refresh() self._stubs: Optional[StubOutForTesting] = None self.fs: Optional[FakeFilesystem] = None self.fake_modules: Dict[str, Any] = {} self.unfaked_modules: Dict[str, Any] = {} # _isStale is set by tearDown(), reset by _refresh() self._isStale = True self._dyn_patcher: Optional[DynamicPatcher] = None self._patching = False def clear_cache(self) -> None: """Clear the module cache.""" self.__class__.CACHED_MODULES = set() self.__class__.FS_MODULES = {} self.__class__.FS_FUNCTIONS = {} self.__class__.FS_DEFARGS = [] self.__class__.SKIPPED_FS_MODULES = {} def _init_fake_module_classes(self) -> None: # IMPORTANT TESTING NOTE: Whenever you add a new module below, test # it by adding an attribute in fixtures/module_with_attributes.py # and a test in fake_filesystem_unittest_test.py, class # TestAttributesWithFakeModuleNames. self._fake_module_classes = { 'os': fake_filesystem.FakeOsModule, 'shutil': fake_filesystem_shutil.FakeShutilModule, 'io': fake_filesystem.FakeIoModule, 'pathlib': fake_pathlib.FakePathlibModule } if IS_PYPY: # in PyPy io.open, the module is referenced as _io self._fake_module_classes['_io'] = fake_filesystem.FakeIoModule if sys.platform != 'win32': self._fake_module_classes[ 'fcntl'] = fake_filesystem.FakeFcntlModule # class modules maps class names against a list of modules they can # be contained in - this allows for alternative modules like # `pathlib` and `pathlib2` self._class_modules['Path'] = ['pathlib'] self._unfaked_module_classes[ 'pathlib'] = fake_pathlib.RealPathlibModule if pathlib2: self._fake_module_classes[ 'pathlib2'] = fake_pathlib.FakePathlibModule self._class_modules['Path'].append('pathlib2') self._unfaked_module_classes[ 'pathlib2'] = fake_pathlib.RealPathlibModule self._fake_module_classes[ 'Path'] = fake_pathlib.FakePathlibPathModule self._unfaked_module_classes[ 'Path'] = fake_pathlib.RealPathlibPathModule if use_scandir: self._fake_module_classes[ 'scandir'] = fake_scandir.FakeScanDirModule def _init_fake_module_functions(self) -> None: # handle patching function imported separately like # `from os import stat` # each patched function name has to be looked up separately for mod_name, fake_module in self._fake_module_classes.items(): if (hasattr(fake_module, 'dir') and inspect.isfunction(fake_module.dir)): for fct_name in fake_module.dir(): module_attr = (getattr(fake_module, fct_name), mod_name) self._fake_module_functions.setdefault( fct_name, {})[mod_name] = module_attr if mod_name == 'os': self._fake_module_functions.setdefault( fct_name, {})[OS_MODULE] = module_attr # special handling for functions in os.path fake_module = fake_filesystem.FakePathModule for fct_name in fake_module.dir(): module_attr = (getattr(fake_module, fct_name), PATH_MODULE) self._fake_module_functions.setdefault( fct_name, {})['genericpath'] = module_attr self._fake_module_functions.setdefault( fct_name, {})[PATH_MODULE] = module_attr def __enter__(self) -> 'Patcher': """Context manager for usage outside of fake_filesystem_unittest.TestCase. Ensure that all patched modules are removed in case of an unhandled exception. """ self.setUp() return self def __exit__(self, exc_type: Optional[Type[BaseException]], exc_val: Optional[BaseException], exc_tb: Optional[TracebackType]) -> None: self.tearDown() def _is_fs_module(self, mod: ModuleType, name: str, module_names: List[str]) -> bool: try: # check for __name__ first and ignore the AttributeException # if it does not exist - avoids calling expansive ismodule if mod.__name__ in module_names and inspect.ismodule(mod): return True except Exception: pass try: if (name in self._class_modules and mod.__module__ in self._class_modules[name]): return inspect.isclass(mod) except Exception: # handle AttributeError and any other exception possibly triggered # by side effects of inspect methods pass return False def _is_fs_function(self, fct: FunctionType) -> bool: try: # check for __name__ first and ignore the AttributeException # if it does not exist - avoids calling expansive inspect # methods in most cases return (fct.__name__ in self._fake_module_functions and fct.__module__ in self._fake_module_functions[ fct.__name__] and (inspect.isfunction(fct) or inspect.isbuiltin(fct))) except Exception: # handle AttributeError and any other exception possibly triggered # by side effects of inspect methods return False def _def_values( self, item: FunctionType) -> Iterator[Tuple[FunctionType, int, Any]]: """Find default arguments that are file-system functions to be patched in top-level functions and members of top-level classes.""" # check for module-level functions try: if item.__defaults__ and inspect.isfunction(item): for i, d in enumerate(item.__defaults__): if self._is_fs_function(d): yield item, i, d except Exception: pass try: if inspect.isclass(item): # check for methods in class # (nested classes are ignored for now) # inspect.getmembers is very expansive! for m in inspect.getmembers(item, predicate=inspect.isfunction): f = cast(FunctionType, m[1]) if f.__defaults__: for i, d in enumerate(f.__defaults__): if self._is_fs_function(d): yield f, i, d except Exception: # Ignore any exception, examples: # ImportError: No module named '_gdbm' # _DontDoThat() (see #523) pass def _find_def_values( self, module_items: ItemsView[str, FunctionType]) -> None: for _, fct in module_items: for f, i, d in self._def_values(fct): self.__class__.FS_DEFARGS.append((f, i, d)) def _find_modules(self) -> None: """Find and cache all modules that import file system modules. Later, `setUp()` will stub these with the fake file system modules. """ module_names = list(self._fake_module_classes.keys()) + [PATH_MODULE] for name, module in list(sys.modules.items()): try: if (self.use_cache and module in self.CACHED_MODULES or module in self.SKIPMODULES or not inspect.ismodule(module)): continue except Exception: # workaround for some py (part of pytest) versions # where py.error has no __name__ attribute # see https://github.com/pytest-dev/py/issues/73 # and any other exception triggered by inspect.ismodule if self.use_cache: self.__class__.CACHED_MODULES.add(module) continue skipped = (any([sn.startswith(module.__name__) for sn in self._skip_names])) module_items = module.__dict__.copy().items() modules = {name: mod for name, mod in module_items if self._is_fs_module(mod, name, module_names)} if skipped: for name, mod in modules.items(): self.__class__.SKIPPED_FS_MODULES.setdefault( name, set()).add((module, mod.__name__)) continue for name, mod in modules.items(): self.__class__.FS_MODULES.setdefault(name, set()).add( (module, mod.__name__)) functions = {name: fct for name, fct in module_items if self._is_fs_function(fct)} for name, fct in functions.items(): self.__class__.FS_FUNCTIONS.setdefault( (name, fct.__name__, fct.__module__), set()).add(module) # find default arguments that are file system functions if self.patch_default_args: self._find_def_values(module_items) if self.use_cache: self.__class__.CACHED_MODULES.add(module) def _refresh(self) -> None: """Renew the fake file system and set the _isStale flag to `False`.""" if self._stubs is not None: self._stubs.smart_unset_all() self._stubs = mox3_stubout.StubOutForTesting() self.fs = fake_filesystem.FakeFilesystem(patcher=self) self.fs.patch_open_code = self.patch_open_code for name in self._fake_module_classes: self.fake_modules[name] = self._fake_module_classes[name](self.fs) if hasattr(self.fake_modules[name], 'skip_names'): self.fake_modules[name].skip_names = self._skip_names self.fake_modules[PATH_MODULE] = self.fake_modules['os'].path for name in self._unfaked_module_classes: self.unfaked_modules[name] = self._unfaked_module_classes[name]() self._isStale = False def setUp(self, doctester: Any = None) -> None: """Bind the file-related modules to the :py:mod:`pyfakefs` fake modules real ones. Also bind the fake `file()` and `open()` functions. """ self.has_fcopy_file = (sys.platform == 'darwin' and hasattr(shutil, '_HAS_FCOPYFILE') and shutil._HAS_FCOPYFILE) if self.has_fcopy_file: shutil._HAS_FCOPYFILE = False # type: ignore[attr-defined] temp_dir = tempfile.gettempdir() with warnings.catch_warnings(): # ignore warnings, see #542 and #614 warnings.filterwarnings( 'ignore' ) self._find_modules() self._refresh() if doctester is not None: doctester.globs = self.replace_globs(doctester.globs) self.start_patching() linecache.open = self.original_open # type: ignore[attr-defined] tokenize._builtin_open = self.original_open # type: ignore # the temp directory is assumed to exist at least in `tempfile1`, # so we create it here for convenience assert self.fs is not None self.fs.create_dir(temp_dir) def start_patching(self) -> None: if not self._patching: self._patching = True self.patch_modules() self.patch_functions() self.patch_defaults() self._dyn_patcher = DynamicPatcher(self) sys.meta_path.insert(0, self._dyn_patcher) for module in self.modules_to_reload: if sys.modules.get(module.__name__) is module: reload(module) def patch_functions(self) -> None: assert self._stubs is not None for (name, ft_name, ft_mod), modules in self.FS_FUNCTIONS.items(): method, mod_name = self._fake_module_functions[ft_name][ft_mod] fake_module = self.fake_modules[mod_name] attr = method.__get__(fake_module, fake_module.__class__) for module in modules: self._stubs.smart_set(module, name, attr) def patch_modules(self) -> None: assert self._stubs is not None for name, modules in self.FS_MODULES.items(): for module, attr in modules: self._stubs.smart_set( module, name, self.fake_modules[attr]) for name, modules in self.SKIPPED_FS_MODULES.items(): for module, attr in modules: if attr in self.unfaked_modules: self._stubs.smart_set( module, name, self.unfaked_modules[attr]) def patch_defaults(self) -> None: for (fct, idx, ft) in self.FS_DEFARGS: method, mod_name = self._fake_module_functions[ ft.__name__][ft.__module__] fake_module = self.fake_modules[mod_name] attr = method.__get__(fake_module, fake_module.__class__) new_defaults = [] assert fct.__defaults__ is not None for i, d in enumerate(fct.__defaults__): if i == idx: new_defaults.append(attr) else: new_defaults.append(d) fct.__defaults__ = tuple(new_defaults) def replace_globs(self, globs_: Dict[str, Any]) -> Dict[str, Any]: globs = globs_.copy() if self._isStale: self._refresh() for name in self._fake_module_classes: if name in globs: globs[name] = self._fake_module_classes[name](self.fs) return globs def tearDown(self, doctester: Any = None): """Clear the fake filesystem bindings created by `setUp()`.""" self.stop_patching() if self.has_fcopy_file: shutil._HAS_FCOPYFILE = True # type: ignore[attr-defined] reset_ids() def stop_patching(self) -> None: if self._patching: self._isStale = True self._patching = False if self._stubs: self._stubs.smart_unset_all() self.unset_defaults() if self._dyn_patcher: self._dyn_patcher.cleanup() sys.meta_path.pop(0) def unset_defaults(self) -> None: for (fct, idx, ft) in self.FS_DEFARGS: new_defaults = [] for i, d in enumerate(cast(Tuple, fct.__defaults__)): if i == idx: new_defaults.append(ft) else: new_defaults.append(d) fct.__defaults__ = tuple(new_defaults) def pause(self) -> None: """Pause the patching of the file system modules until `resume` is called. After that call, all file system calls are executed in the real file system. Calling pause() twice is silently ignored. """ self.stop_patching() def resume(self) -> None: """Resume the patching of the file system modules if `pause` has been called before. After that call, all file system calls are executed in the fake file system. Does nothing if patching is not paused. """ self.start_patching() class Pause: """Simple context manager that allows to pause/resume patching the filesystem. Patching is paused in the context manager, and resumed after going out of it's scope. """ def __init__(self, caller: Union[Patcher, TestCaseMixin, FakeFilesystem]): """Initializes the context manager with the fake filesystem. Args: caller: either the FakeFilesystem instance, the Patcher instance or the pyfakefs test case. """ if isinstance(caller, (Patcher, TestCaseMixin)): assert caller.fs is not None self._fs: FakeFilesystem = caller.fs elif isinstance(caller, FakeFilesystem): self._fs = caller else: raise ValueError('Invalid argument - should be of type ' '"fake_filesystem_unittest.Patcher", ' '"fake_filesystem_unittest.TestCase" ' 'or "fake_filesystem.FakeFilesystem"') def __enter__(self) -> FakeFilesystem: self._fs.pause() return self._fs def __exit__(self, *args: Any) -> None: self._fs.resume() class DynamicPatcher(MetaPathFinder, Loader): """A file loader that replaces file system related modules by their fake implementation if they are loaded after calling `setUpPyfakefs()`. Implements the protocol needed for import hooks. """ def __init__(self, patcher: Patcher) -> None: self._patcher = patcher self.sysmodules = {} self.modules = self._patcher.fake_modules self._loaded_module_names: Set[str] = set() # remove all modules that have to be patched from `sys.modules`, # otherwise the find_... methods will not be called for name in self.modules: if self.needs_patch(name) and name in sys.modules: self.sysmodules[name] = sys.modules[name] del sys.modules[name] for name, module in self.modules.items(): sys.modules[name] = module def cleanup(self) -> None: for module_name in self.sysmodules: sys.modules[module_name] = self.sysmodules[module_name] for module in self._patcher.modules_to_reload: if module.__name__ in sys.modules: reload(module) reloaded_module_names = [module.__name__ for module in self._patcher.modules_to_reload] # Dereference all modules loaded during the test so they will reload on # the next use, ensuring that no faked modules are referenced after the # test. for name in self._loaded_module_names: if name in sys.modules and name not in reloaded_module_names: del sys.modules[name] def needs_patch(self, name: str) -> bool: """Check if the module with the given name shall be replaced.""" if name not in self.modules: self._loaded_module_names.add(name) return False if (name in sys.modules and type(sys.modules[name]) == self.modules[name]): return False return True def find_spec(self, fullname: str, path: Optional[Sequence[Union[bytes, str]]], target: Optional[ModuleType] = None) -> Optional[ModuleSpec]: """Module finder.""" if self.needs_patch(fullname): return ModuleSpec(fullname, self) return None def load_module(self, fullname: str) -> ModuleType: """Replaces the module by its fake implementation.""" sys.modules[fullname] = self.modules[fullname] return self.modules[fullname] pyfakefs-4.5.4/pyfakefs/fake_pathlib.py0000666000000000000000000007500414147521400016265 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A fake implementation for pathlib working with FakeFilesystem. New in pyfakefs 3.0. Usage: * With fake_filesystem_unittest: If using fake_filesystem_unittest.TestCase, pathlib gets replaced by fake_pathlib together with other file system related modules. * Stand-alone with FakeFilesystem: `filesystem = fake_filesystem.FakeFilesystem()` `fake_pathlib_module = fake_filesystem.FakePathlibModule(filesystem)` `path = fake_pathlib_module.Path('/foo/bar')` Note: as the implementation is based on FakeFilesystem, all faked classes (including PurePosixPath, PosixPath, PureWindowsPath and WindowsPath) get the properties of the underlying fake filesystem. """ import errno import fnmatch import functools import os import pathlib from pathlib import PurePath import re import sys from urllib.parse import quote_from_bytes as urlquote_from_bytes from pyfakefs import fake_scandir from pyfakefs.extra_packages import use_scandir from pyfakefs.fake_filesystem import FakeFileOpen, FakeFilesystem def init_module(filesystem): """Initializes the fake module with the fake file system.""" # pylint: disable=protected-access FakePath.filesystem = filesystem FakePathlibModule.PureWindowsPath._flavour = _FakeWindowsFlavour( filesystem) FakePathlibModule.PurePosixPath._flavour = _FakePosixFlavour(filesystem) def _wrap_strfunc(strfunc): @functools.wraps(strfunc) def _wrapped(pathobj, *args, **kwargs): return strfunc(pathobj.filesystem, str(pathobj), *args, **kwargs) return staticmethod(_wrapped) def _wrap_binary_strfunc(strfunc): @functools.wraps(strfunc) def _wrapped(pathobj1, pathobj2, *args): return strfunc( pathobj1.filesystem, str(pathobj1), str(pathobj2), *args) return staticmethod(_wrapped) def _wrap_binary_strfunc_reverse(strfunc): @functools.wraps(strfunc) def _wrapped(pathobj1, pathobj2, *args): return strfunc( pathobj2.filesystem, str(pathobj2), str(pathobj1), *args) return staticmethod(_wrapped) try: accessor = pathlib._Accessor # type: ignore [attr-defined] except AttributeError: accessor = object class _FakeAccessor(accessor): # type: ignore [valid-type, misc] """Accessor which forwards some of the functions to FakeFilesystem methods. """ stat = _wrap_strfunc(FakeFilesystem.stat) lstat = _wrap_strfunc( lambda fs, path: FakeFilesystem.stat(fs, path, follow_symlinks=False)) listdir = _wrap_strfunc(FakeFilesystem.listdir) if use_scandir: scandir = _wrap_strfunc(fake_scandir.scandir) chmod = _wrap_strfunc(FakeFilesystem.chmod) if hasattr(os, "lchmod"): lchmod = _wrap_strfunc(lambda fs, path, mode: FakeFilesystem.chmod( fs, path, mode, follow_symlinks=False)) else: def lchmod(self, pathobj, *args, **kwargs): """Raises not implemented for Windows systems.""" raise NotImplementedError("lchmod() not available on this system") def chmod(self, pathobj, *args, **kwargs): if "follow_symlinks" in kwargs: if sys.version_info < (3, 10): raise TypeError("chmod() got an unexpected keyword " "argument 'follow_synlinks'") if (not kwargs["follow_symlinks"] and os.chmod not in os.supports_follow_symlinks): raise NotImplementedError( "`follow_symlinks` for chmod() is not available " "on this system") return pathobj.filesystem.chmod(str(pathobj), *args, **kwargs) mkdir = _wrap_strfunc(FakeFilesystem.makedir) unlink = _wrap_strfunc(FakeFilesystem.remove) rmdir = _wrap_strfunc(FakeFilesystem.rmdir) rename = _wrap_binary_strfunc(FakeFilesystem.rename) replace = _wrap_binary_strfunc( lambda fs, old_path, new_path: FakeFilesystem.rename( fs, old_path, new_path, force_replace=True)) symlink = _wrap_binary_strfunc_reverse( lambda fs, file_path, link_target, target_is_directory: FakeFilesystem.create_symlink(fs, file_path, link_target, create_missing_dirs=False)) if (3, 8) <= sys.version_info: link_to = _wrap_binary_strfunc( lambda fs, file_path, link_target: FakeFilesystem.link(fs, file_path, link_target)) if sys.version_info >= (3, 10): link = _wrap_binary_strfunc( lambda fs, file_path, link_target: FakeFilesystem.link(fs, file_path, link_target)) # this will use the fake filesystem because os is patched def getcwd(self): return os.getcwd() readlink = _wrap_strfunc(FakeFilesystem.readlink) utime = _wrap_strfunc(FakeFilesystem.utime) _fake_accessor = _FakeAccessor() flavour = pathlib._Flavour # type: ignore [attr-defined] class _FakeFlavour(flavour): # type: ignore [valid-type, misc] """Fake Flavour implementation used by PurePath and _Flavour""" filesystem = None sep = '/' altsep = None has_drv = False ext_namespace_prefix = '\\\\?\\' drive_letters = ( set(chr(x) for x in range(ord('a'), ord('z') + 1)) | set(chr(x) for x in range(ord('A'), ord('Z') + 1)) ) def __init__(self, filesystem): self.filesystem = filesystem self.sep = filesystem.path_separator self.altsep = filesystem.alternative_path_separator self.has_drv = filesystem.is_windows_fs super(_FakeFlavour, self).__init__() @staticmethod def _split_extended_path(path, ext_prefix=ext_namespace_prefix): prefix = '' if path.startswith(ext_prefix): prefix = path[:4] path = path[4:] if path.startswith('UNC\\'): prefix += path[:3] path = '\\' + path[3:] return prefix, path def _splitroot_with_drive(self, path, sep): first = path[0:1] second = path[1:2] if second == sep and first == sep: # extended paths should also disable the collapsing of "." # components (according to MSDN docs). prefix, path = self._split_extended_path(path) first = path[0:1] second = path[1:2] else: prefix = '' third = path[2:3] if second == sep and first == sep and third != sep: # is a UNC path: # vvvvvvvvvvvvvvvvvvvvv root # \\machine\mountpoint\directory\etc\... # directory ^^^^^^^^^^^^^^ index = path.find(sep, 2) if index != -1: index2 = path.find(sep, index + 1) # a UNC path can't have two slashes in a row # (after the initial two) if index2 != index + 1: if index2 == -1: index2 = len(path) if prefix: return prefix + path[1:index2], sep, path[index2 + 1:] return path[:index2], sep, path[index2 + 1:] drv = root = '' if second == ':' and first in self.drive_letters: drv = path[:2] path = path[2:] first = third if first == sep: root = first path = path.lstrip(sep) return prefix + drv, root, path @staticmethod def _splitroot_posix(path, sep): if path and path[0] == sep: stripped_part = path.lstrip(sep) if len(path) - len(stripped_part) == 2: return '', sep * 2, stripped_part return '', sep, stripped_part else: return '', '', path def splitroot(self, path, sep=None): """Split path into drive, root and rest.""" if sep is None: sep = self.filesystem.path_separator if self.filesystem.is_windows_fs: return self._splitroot_with_drive(path, sep) return self._splitroot_posix(path, sep) def casefold(self, path): """Return the lower-case version of s for a Windows filesystem.""" if self.filesystem.is_windows_fs: return path.lower() return path def casefold_parts(self, parts): """Return the lower-case version of parts for a Windows filesystem.""" if self.filesystem.is_windows_fs: return [p.lower() for p in parts] return parts def _resolve_posix(self, path, strict): sep = self.sep seen = {} def _resolve(path, rest): if rest.startswith(sep): path = '' for name in rest.split(sep): if not name or name == '.': # current dir continue if name == '..': # parent dir path, _, _ = path.rpartition(sep) continue newpath = path + sep + name if newpath in seen: # Already seen this path path = seen[newpath] if path is not None: # use cached value continue # The symlink is not resolved, so we must have # a symlink loop. raise RuntimeError("Symlink loop from %r" % newpath) # Resolve the symbolic link try: target = self.filesystem.readlink(newpath) except OSError as e: if e.errno != errno.EINVAL and strict: raise # Not a symlink, or non-strict mode. We just leave the path # untouched. path = newpath else: seen[newpath] = None # not resolved symlink path = _resolve(path, target) seen[newpath] = path # resolved symlink return path # NOTE: according to POSIX, getcwd() cannot contain path components # which are symlinks. base = '' if path.is_absolute() else self.filesystem.cwd return _resolve(base, str(path)) or sep def _resolve_windows(self, path, strict): path = str(path) if not path: return os.getcwd() previous_s = None if strict: if not self.filesystem.exists(path): self.filesystem.raise_os_error(errno.ENOENT, path) return self.filesystem.resolve_path(path) else: while True: try: path = self.filesystem.resolve_path(path) except OSError: previous_s = path path = self.filesystem.splitpath(path)[0] else: if previous_s is None: return path return self.filesystem.joinpaths( path, os.path.basename(previous_s)) def resolve(self, path, strict): """Make the path absolute, resolving any symlinks.""" if self.filesystem.is_windows_fs: return self._resolve_windows(path, strict) return self._resolve_posix(path, strict) def gethomedir(self, username): """Return the home directory of the current user.""" if not username: try: return os.environ['HOME'] except KeyError: import pwd return pwd.getpwuid(os.getuid()).pw_dir else: import pwd try: return pwd.getpwnam(username).pw_dir except KeyError: raise RuntimeError("Can't determine home directory " "for %r" % username) class _FakeWindowsFlavour(_FakeFlavour): """Flavour used by PureWindowsPath with some Windows specific implementations independent of FakeFilesystem properties. """ reserved_names = ( {'CON', 'PRN', 'AUX', 'NUL'} | {'COM%d' % i for i in range(1, 10)} | {'LPT%d' % i for i in range(1, 10)} ) def is_reserved(self, parts): """Return True if the path is considered reserved under Windows.""" # NOTE: the rules for reserved names seem somewhat complicated # (e.g. r"..\NUL" is reserved but not r"foo\NUL"). # We err on the side of caution and return True for paths which are # not considered reserved by Windows. if not parts: return False if self.filesystem.is_windows_fs and parts[0].startswith('\\\\'): # UNC paths are never reserved return False return parts[-1].partition('.')[0].upper() in self.reserved_names def make_uri(self, path): """Return a file URI for the given path""" # Under Windows, file URIs use the UTF-8 encoding. # original version, not faked drive = path.drive if len(drive) == 2 and drive[1] == ':': # It's a path on a local drive => 'file:///c:/a/b' rest = path.as_posix()[2:].lstrip('/') return 'file:///%s/%s' % ( drive, urlquote_from_bytes(rest.encode('utf-8'))) else: # It's a path on a network drive => 'file://host/share/a/b' return ('file:' + urlquote_from_bytes(path.as_posix().encode('utf-8'))) def gethomedir(self, username): """Return the home directory of the current user.""" # original version, not faked if 'HOME' in os.environ: userhome = os.environ['HOME'] elif 'USERPROFILE' in os.environ: userhome = os.environ['USERPROFILE'] elif 'HOMEPATH' in os.environ: try: drv = os.environ['HOMEDRIVE'] except KeyError: drv = '' userhome = drv + os.environ['HOMEPATH'] else: raise RuntimeError("Can't determine home directory") if username: # Try to guess user home directory. By default all users # directories are located in the same place and are named by # corresponding usernames. If current user home directory points # to nonstandard place, this guess is likely wrong. if os.environ['USERNAME'] != username: drv, root, parts = self.parse_parts((userhome,)) if parts[-1] != os.environ['USERNAME']: raise RuntimeError("Can't determine home directory " "for %r" % username) parts[-1] = username if drv or root: userhome = drv + root + self.join(parts[1:]) else: userhome = self.join(parts) return userhome def compile_pattern(self, pattern): return re.compile(fnmatch.translate(pattern), re.IGNORECASE).fullmatch class _FakePosixFlavour(_FakeFlavour): """Flavour used by PurePosixPath with some Unix specific implementations independent of FakeFilesystem properties. """ def is_reserved(self, parts): return False def make_uri(self, path): # We represent the path using the local filesystem encoding, # for portability to other applications. bpath = bytes(path) return 'file://' + urlquote_from_bytes(bpath) def gethomedir(self, username): # original version, not faked if not username: try: return os.environ['HOME'] except KeyError: import pwd return pwd.getpwuid(os.getuid()).pw_dir else: import pwd try: return pwd.getpwnam(username).pw_dir except KeyError: raise RuntimeError("Can't determine home directory " "for %r" % username) def compile_pattern(self, pattern): return re.compile(fnmatch.translate(pattern)).fullmatch class FakePath(pathlib.Path): """Replacement for pathlib.Path. Reimplement some methods to use fake filesystem. The rest of the methods work as they are, as they will use the fake accessor. New in pyfakefs 3.0. """ # the underlying fake filesystem filesystem = None def __new__(cls, *args, **kwargs): """Creates the correct subclass based on OS.""" if cls is FakePathlibModule.Path: cls = (FakePathlibModule.WindowsPath if cls.filesystem.is_windows_fs else FakePathlibModule.PosixPath) self = cls._from_parts(args) return self @classmethod def _from_parts(cls, args, init=False): # pylint: disable=unused-argument # Overwritten to call _init to set the fake accessor, # which is not done since Python 3.10 self = object.__new__(cls) self._init() drv, root, parts = self._parse_args(args) self._drv = drv self._root = root self._parts = parts return self @classmethod def _from_parsed_parts(cls, drv, root, parts): # Overwritten to call _init to set the fake accessor, # which is not done since Python 3.10 self = object.__new__(cls) self._init() self._drv = drv self._root = root self._parts = parts return self def _init(self, template=None): """Initializer called from base class.""" self._accessor = _fake_accessor self._closed = False def _path(self): """Returns the underlying path string as used by the fake filesystem. """ return str(self) @classmethod def cwd(cls): """Return a new path pointing to the current working directory (as returned by os.getcwd()). """ return cls(cls.filesystem.cwd) def resolve(self, strict=None): """Make the path absolute, resolving all symlinks on the way and also normalizing it (for example turning slashes into backslashes under Windows). Args: strict: If False (default) no exception is raised if the path does not exist. New in Python 3.6. Raises: OSError: if the path doesn't exist (strict=True or Python < 3.6) """ if sys.version_info >= (3, 6): if strict is None: strict = False else: if strict is not None: raise TypeError( "resolve() got an unexpected keyword argument 'strict'") strict = True if self._closed: self._raise_closed() path = self._flavour.resolve(self, strict=strict) if path is None: self.stat() path = str(self.absolute()) path = self.filesystem.absnormpath(path) return FakePath(path) def open(self, mode='r', buffering=-1, encoding=None, errors=None, newline=None): """Open the file pointed by this path and return a fake file object. Raises: OSError: if the target object is a directory, the path is invalid or permission is denied. """ if self._closed: self._raise_closed() return FakeFileOpen(self.filesystem)( self._path(), mode, buffering, encoding, errors, newline) def read_bytes(self): """Open the fake file in bytes mode, read it, and close the file. Raises: OSError: if the target object is a directory, the path is invalid or permission is denied. """ with FakeFileOpen(self.filesystem)(self._path(), mode='rb') as f: return f.read() def read_text(self, encoding=None, errors=None): """ Open the fake file in text mode, read it, and close the file. """ with FakeFileOpen(self.filesystem)(self._path(), mode='r', encoding=encoding, errors=errors) as f: return f.read() def write_bytes(self, data): """Open the fake file in bytes mode, write to it, and close the file. Args: data: the bytes to be written Raises: OSError: if the target object is a directory, the path is invalid or permission is denied. """ # type-check for the buffer interface before truncating the file view = memoryview(data) with FakeFileOpen(self.filesystem)(self._path(), mode='wb') as f: return f.write(view) def write_text(self, data, encoding=None, errors=None, newline=None): """Open the fake file in text mode, write to it, and close the file. Args: data: the string to be written encoding: the encoding used for the string; if not given, the default locale encoding is used errors: (str) Defines how encoding errors are handled. newline: Controls universal newlines, passed to stream object. New in Python 3.10. Raises: TypeError: if data is not of type 'str'. OSError: if the target object is a directory, the path is invalid or permission is denied. """ if not isinstance(data, str): raise TypeError('data must be str, not %s' % data.__class__.__name__) if newline is not None and sys.version_info < (3, 10): raise TypeError("write_text() got an unexpected " "keyword argument 'newline'") with FakeFileOpen(self.filesystem)(self._path(), mode='w', encoding=encoding, errors=errors, newline=newline) as f: return f.write(data) @classmethod def home(cls): """Return a new path pointing to the user's home directory (as returned by os.path.expanduser('~')). """ home = os.path.expanduser("~") if cls.filesystem.is_windows_fs != (os.name == 'nt'): username = os.path.split(home)[1] if cls.filesystem.is_windows_fs: home = os.path.join('C:', 'Users', username) else: home = os.path.join('home', username) cls.filesystem.create_dir(home) return cls(home.replace(os.sep, cls.filesystem.path_separator)) def samefile(self, other_path): """Return whether other_path is the same or not as this file (as returned by os.path.samefile()). Args: other_path: A path object or string of the file object to be compared with Raises: OSError: if the filesystem object doesn't exist. """ st = self.stat() try: other_st = other_path.stat() except AttributeError: other_st = self.filesystem.stat(other_path) return (st.st_ino == other_st.st_ino and st.st_dev == other_st.st_dev) def expanduser(self): """ Return a new path with expanded ~ and ~user constructs (as returned by os.path.expanduser) """ return FakePath(os.path.expanduser(self._path()) .replace(os.path.sep, self.filesystem.path_separator)) def touch(self, mode=0o666, exist_ok=True): """Create a fake file for the path with the given access mode, if it doesn't exist. Args: mode: the file mode for the file if it does not exist exist_ok: if the file already exists and this is True, nothing happens, otherwise FileExistError is raised Raises: FileExistsError: if the file exists and exits_ok is False. """ if self._closed: self._raise_closed() if self.exists(): if exist_ok: self.filesystem.utime(self._path(), times=None) else: self.filesystem.raise_os_error(errno.EEXIST, self._path()) else: fake_file = self.open('w') fake_file.close() self.chmod(mode) class FakePathlibModule: """Uses FakeFilesystem to provide a fake pathlib module replacement. Can be used to replace both the standard `pathlib` module and the `pathlib2` package available on PyPi. You need a fake_filesystem to use this: `filesystem = fake_filesystem.FakeFilesystem()` `fake_pathlib_module = fake_filesystem.FakePathlibModule(filesystem)` """ def __init__(self, filesystem): """ Initializes the module with the given filesystem. Args: filesystem: FakeFilesystem used to provide file system information """ init_module(filesystem) self._pathlib_module = pathlib class PurePosixPath(PurePath): """A subclass of PurePath, that represents non-Windows filesystem paths""" __slots__ = () class PureWindowsPath(PurePath): """A subclass of PurePath, that represents Windows filesystem paths""" __slots__ = () class WindowsPath(FakePath, PureWindowsPath): """A subclass of Path and PureWindowsPath that represents concrete Windows filesystem paths. """ __slots__ = () def owner(self): raise NotImplementedError( "Path.owner() is unsupported on this system") def group(self): raise NotImplementedError( "Path.group() is unsupported on this system") def is_mount(self): raise NotImplementedError( "Path.is_mount() is unsupported on this system") class PosixPath(FakePath, PurePosixPath): """A subclass of Path and PurePosixPath that represents concrete non-Windows filesystem paths. """ __slots__ = () def owner(self): """Return the current user name. It is assumed that the fake file system was created by the current user. """ import pwd return pwd.getpwuid(os.getuid()).pw_name def group(self): """Return the current group name. It is assumed that the fake file system was created by the current user. """ import grp return grp.getgrgid(os.getgid()).gr_name Path = FakePath def __getattr__(self, name): """Forwards any unfaked calls to the standard pathlib module.""" return getattr(self._pathlib_module, name) class FakePathlibPathModule: """Patches `pathlib.Path` by passing all calls to FakePathlibModule.""" fake_pathlib = None def __init__(self, filesystem=None): if self.fake_pathlib is None: self.__class__.fake_pathlib = FakePathlibModule(filesystem) def __call__(self, *args, **kwargs): return self.fake_pathlib.Path(*args, **kwargs) def __getattr__(self, name): return getattr(self.fake_pathlib.Path, name) class RealPath(pathlib.Path): """Replacement for `pathlib.Path` if it shall not be faked. Needed because `Path` in `pathlib` is always faked, even if `pathlib` itself is not. """ def __new__(cls, *args, **kwargs): """Creates the correct subclass based on OS.""" if cls is RealPathlibModule.Path: cls = (RealPathlibModule.WindowsPath if os.name == 'nt' else RealPathlibModule.PosixPath) self = cls._from_parts(args) return self class RealPathlibModule: """Used to replace `pathlib` for skipped modules. As the original `pathlib` is always patched to use the fake path, we need to provide a version which does not do this. """ def __init__(self): RealPathlibModule.PureWindowsPath._flavour = pathlib._WindowsFlavour() RealPathlibModule.PurePosixPath._flavour = pathlib._PosixFlavour() self._pathlib_module = pathlib class PurePosixPath(PurePath): """A subclass of PurePath, that represents Posix filesystem paths""" __slots__ = () class PureWindowsPath(PurePath): """A subclass of PurePath, that represents Windows filesystem paths""" __slots__ = () if sys.platform == 'win32': class WindowsPath(RealPath, PureWindowsPath): """A subclass of Path and PureWindowsPath that represents concrete Windows filesystem paths. """ __slots__ = () else: class PosixPath(RealPath, PurePosixPath): """A subclass of Path and PurePosixPath that represents concrete non-Windows filesystem paths. """ __slots__ = () Path = RealPath def __getattr__(self, name): """Forwards any unfaked calls to the standard pathlib module.""" return getattr(self._pathlib_module, name) class RealPathlibPathModule: """Patches `pathlib.Path` by passing all calls to RealPathlibModule.""" real_pathlib = None def __init__(self): if self.real_pathlib is None: self.__class__.real_pathlib = RealPathlibModule() def __call__(self, *args, **kwargs): return self.real_pathlib.Path(*args, **kwargs) def __getattr__(self, name): return getattr(self.real_pathlib.Path, name) pyfakefs-4.5.4/pyfakefs/fake_scandir.py0000666000000000000000000002604114147521400016262 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A fake implementation for the `scandir` function working with FakeFilesystem. Works with both the function integrated into the `os` module since Python 3.5 and the standalone function available in the standalone `scandir` python package. """ import os import sys from pyfakefs.extra_packages import use_scandir_package from pyfakefs.helpers import to_string if sys.version_info >= (3, 6): BaseClass = os.PathLike else: BaseClass = object class DirEntry(BaseClass): """Emulates os.DirEntry. Note that we did not enforce keyword only arguments.""" def __init__(self, filesystem): """Initialize the dir entry with unset values. Args: filesystem: the fake filesystem used for implementation. """ self._filesystem = filesystem self.name = '' self.path = '' self._abspath = '' self._inode = None self._islink = False self._isdir = False self._statresult = None self._statresult_symlink = None def inode(self): """Return the inode number of the entry.""" if self._inode is None: self.stat(follow_symlinks=False) return self._inode def is_dir(self, follow_symlinks=True): """Return True if this entry is a directory entry. Args: follow_symlinks: If True, also return True if this entry is a symlink pointing to a directory. Returns: True if this entry is an existing directory entry, or if follow_symlinks is set, and this entry points to an existing directory entry. """ return self._isdir and (follow_symlinks or not self._islink) def is_file(self, follow_symlinks=True): """Return True if this entry is a regular file entry. Args: follow_symlinks: If True, also return True if this entry is a symlink pointing to a regular file. Returns: True if this entry is an existing file entry, or if follow_symlinks is set, and this entry points to an existing file entry. """ return not self._isdir and (follow_symlinks or not self._islink) def is_symlink(self): """Return True if this entry is a symbolic link (even if broken).""" return self._islink def stat(self, follow_symlinks=True): """Return a stat_result object for this entry. Args: follow_symlinks: If False and the entry is a symlink, return the result for the symlink, otherwise for the object it points to. """ if follow_symlinks: if self._statresult_symlink is None: file_object = self._filesystem.resolve(self._abspath) self._statresult_symlink = file_object.stat_result.copy() if self._filesystem.is_windows_fs: self._statresult_symlink.st_nlink = 0 return self._statresult_symlink if self._statresult is None: file_object = self._filesystem.lresolve(self._abspath) self._inode = file_object.st_ino self._statresult = file_object.stat_result.copy() if self._filesystem.is_windows_fs: self._statresult.st_nlink = 0 return self._statresult if sys.version_info >= (3, 6): def __fspath__(self): return self.path class ScanDirIter: """Iterator for DirEntry objects returned from `scandir()` function.""" def __init__(self, filesystem, path): self.filesystem = filesystem if isinstance(path, int): if not use_scandir_package and ( sys.version_info < (3, 7) or self.filesystem.is_windows_fs): raise NotImplementedError( 'scandir does not support file descriptor ' 'path argument') self.abspath = self.filesystem.absnormpath( self.filesystem.get_open_file(path).get_object().path) self.path = '' else: self.abspath = self.filesystem.absnormpath(path) self.path = to_string(path) entries = self.filesystem.confirmdir(self.abspath).entries self.entry_iter = iter(entries) def __iter__(self): return self def __next__(self): entry = self.entry_iter.__next__() dir_entry = DirEntry(self.filesystem) dir_entry.name = entry dir_entry.path = self.filesystem.joinpaths(self.path, dir_entry.name) dir_entry._abspath = self.filesystem.joinpaths(self.abspath, dir_entry.name) dir_entry._isdir = self.filesystem.isdir(dir_entry._abspath) dir_entry._islink = self.filesystem.islink(dir_entry._abspath) return dir_entry if sys.version_info >= (3, 6): def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): self.close() def close(self): pass def scandir(filesystem, path=''): """Return an iterator of DirEntry objects corresponding to the entries in the directory given by path. Args: filesystem: The fake filesystem used for implementation path: Path to the target directory within the fake filesystem. Returns: an iterator to an unsorted list of os.DirEntry objects for each entry in path. Raises: OSError: if the target is not a directory. """ return ScanDirIter(filesystem, path) def _classify_directory_contents(filesystem, root): """Classify contents of a directory as files/directories. Args: filesystem: The fake filesystem used for implementation root: (str) Directory to examine. Returns: (tuple) A tuple consisting of three values: the directory examined, a list containing all of the directory entries, and a list containing all of the non-directory entries. (This is the same format as returned by the `os.walk` generator.) Raises: Nothing on its own, but be ready to catch exceptions generated by underlying mechanisms like `os.listdir`. """ dirs = [] files = [] for entry in filesystem.listdir(root): if filesystem.isdir(filesystem.joinpaths(root, entry)): dirs.append(entry) else: files.append(entry) return root, dirs, files def walk(filesystem, top, topdown=True, onerror=None, followlinks=False): """Perform an os.walk operation over the fake filesystem. Args: filesystem: The fake filesystem used for implementation top: The root directory from which to begin walk. topdown: Determines whether to return the tuples with the root as the first entry (`True`) or as the last, after all the child directory tuples (`False`). onerror: If not `None`, function which will be called to handle the `os.error` instance provided when `os.listdir()` fails. followlinks: If `True`, symbolic links are followed. Yields: (path, directories, nondirectories) for top and each of its subdirectories. See the documentation for the builtin os module for further details. """ def do_walk(top_dir, top_most=False): if not top_most and not followlinks and filesystem.islink(top_dir): return try: top_contents = _classify_directory_contents(filesystem, top_dir) except OSError as exc: top_contents = None if onerror is not None: onerror(exc) if top_contents is not None: if topdown: yield top_contents for directory in top_contents[1]: path = filesystem.joinpaths(top_dir, directory) if not followlinks and filesystem.islink(path): continue for contents in do_walk(path): yield contents if not topdown: yield top_contents return do_walk(to_string(top), top_most=True) class FakeScanDirModule: """Uses FakeFilesystem to provide a fake `scandir` module replacement. .. Note:: The ``scandir`` function is a part of the standard ``os`` module since Python 3.5. This class handles the separate ``scandir`` module that is available on pypi. You need a fake_filesystem to use this: `filesystem = fake_filesystem.FakeFilesystem()` `fake_scandir_module = fake_filesystem.FakeScanDirModule(filesystem)` """ @staticmethod def dir(): """Return the list of patched function names. Used for patching functions imported from the module. """ return 'scandir', 'walk' def __init__(self, filesystem): self.filesystem = filesystem def scandir(self, path='.'): """Return an iterator of DirEntry objects corresponding to the entries in the directory given by path. Args: path: Path to the target directory within the fake filesystem. Returns: an iterator to an unsorted list of os.DirEntry objects for each entry in path. Raises: OSError: if the target is not a directory. """ return scandir(self.filesystem, path) def walk(self, top, topdown=True, onerror=None, followlinks=False): """Perform a walk operation over the fake filesystem. Args: top: The root directory from which to begin walk. topdown: Determines whether to return the tuples with the root as the first entry (`True`) or as the last, after all the child directory tuples (`False`). onerror: If not `None`, function which will be called to handle the `os.error` instance provided when `os.listdir()` fails. followlinks: If `True`, symbolic links are followed. Yields: (path, directories, nondirectories) for top and each of its subdirectories. See the documentation for the builtin os module for further details. """ return walk(self.filesystem, top, topdown, onerror, followlinks) pyfakefs-4.5.4/pyfakefs/helpers.py0000666000000000000000000002771014166311104015316 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Helper classes use for fake file system implementation.""" import io import locale import os import platform import stat import sys import time from copy import copy from stat import S_IFLNK from typing import Union, Optional, Any, AnyStr, overload, cast IS_PYPY = platform.python_implementation() == 'PyPy' IS_WIN = sys.platform == 'win32' IN_DOCKER = os.path.exists('/.dockerenv') AnyPath = Union[AnyStr, os.PathLike] def is_int_type(val: Any) -> bool: """Return True if `val` is of integer type.""" return isinstance(val, int) def is_byte_string(val: Any) -> bool: """Return True if `val` is a bytes-like object, False for a unicode string.""" return not hasattr(val, 'encode') def is_unicode_string(val: Any) -> bool: """Return True if `val` is a unicode string, False for a bytes-like object.""" return hasattr(val, 'encode') @overload def make_string_path(dir_name: AnyStr) -> AnyStr: ... @overload def make_string_path(dir_name: os.PathLike) -> str: ... def make_string_path(dir_name: AnyPath) -> AnyStr: return cast(AnyStr, os.fspath(dir_name)) def to_string(path: Union[AnyStr, Union[str, bytes]]) -> str: """Return the string representation of a byte string using the preferred encoding, or the string itself if path is a str.""" if isinstance(path, bytes): return path.decode(locale.getpreferredencoding(False)) return path def to_bytes(path: Union[AnyStr, Union[str, bytes]]) -> bytes: """Return the bytes representation of a string using the preferred encoding, or the byte string itself if path is a byte string.""" if isinstance(path, str): return bytes(path, locale.getpreferredencoding(False)) return path def join_strings(s1: AnyStr, s2: AnyStr) -> AnyStr: """This is a bit of a hack to satisfy mypy - may be refactored.""" return s1 + s2 def real_encoding(encoding: Optional[str]) -> Optional[str]: """Since Python 3.10, the new function ``io.text_encoding`` returns "locale" as the encoding if None is defined. This will be handled as no encoding in pyfakefs.""" if sys.version_info >= (3, 10): return encoding if encoding != "locale" else None return encoding def now(): return time.time() @overload def matching_string(matched: bytes, string: AnyStr) -> bytes: ... @overload def matching_string(matched: str, string: AnyStr) -> str: ... @overload def matching_string(matched: AnyStr, string: None) -> None: ... def matching_string( # type: ignore[misc] matched: AnyStr, string: Optional[AnyStr]) -> Optional[AnyStr]: """Return the string as byte or unicode depending on the type of matched, assuming string is an ASCII string. """ if string is None: return string if isinstance(matched, bytes) and isinstance(string, str): return string.encode(locale.getpreferredencoding(False)) return string class FakeStatResult: """Mimics os.stat_result for use as return type of `stat()` and similar. This is needed as `os.stat_result` has no possibility to set nanosecond times directly. """ _stat_float_times: bool = True def __init__(self, is_windows: bool, user_id: int, group_id: int, initial_time: Optional[float] = None): self._use_float: Optional[bool] = None self.st_mode: int = 0 self.st_ino: Optional[int] = None self.st_dev: int = 0 self.st_nlink: int = 0 self.st_uid: int = user_id self.st_gid: int = group_id self._st_size: int = 0 self.is_windows: bool = is_windows self._st_atime_ns: int = int((initial_time or 0) * 1e9) self._st_mtime_ns: int = self._st_atime_ns self._st_ctime_ns: int = self._st_atime_ns @property def use_float(self) -> bool: if self._use_float is None: return self.stat_float_times() return self._use_float @use_float.setter def use_float(self, val: bool) -> None: self._use_float = val def __eq__(self, other: Any) -> bool: return ( isinstance(other, FakeStatResult) and self._st_atime_ns == other._st_atime_ns and self._st_ctime_ns == other._st_ctime_ns and self._st_mtime_ns == other._st_mtime_ns and self.st_size == other.st_size and self.st_gid == other.st_gid and self.st_uid == other.st_uid and self.st_nlink == other.st_nlink and self.st_dev == other.st_dev and self.st_ino == other.st_ino and self.st_mode == other.st_mode ) def __ne__(self, other: Any) -> bool: return not self == other def copy(self) -> "FakeStatResult": """Return a copy where the float usage is hard-coded to mimic the behavior of the real os.stat_result. """ stat_result = copy(self) stat_result.use_float = self.use_float return stat_result def set_from_stat_result(self, stat_result: os.stat_result) -> None: """Set values from a real os.stat_result. Note: values that are controlled by the fake filesystem are not set. This includes st_ino, st_dev and st_nlink. """ self.st_mode = stat_result.st_mode self.st_uid = stat_result.st_uid self.st_gid = stat_result.st_gid self._st_size = stat_result.st_size self._st_atime_ns = stat_result.st_atime_ns self._st_mtime_ns = stat_result.st_mtime_ns self._st_ctime_ns = stat_result.st_ctime_ns @classmethod def stat_float_times(cls, newvalue: Optional[bool] = None) -> bool: """Determine whether a file's time stamps are reported as floats or ints. Calling without arguments returns the current value. The value is shared by all instances of FakeOsModule. Args: newvalue: If `True`, mtime, ctime, atime are reported as floats. Otherwise, they are returned as ints (rounding down). """ if newvalue is not None: cls._stat_float_times = bool(newvalue) return cls._stat_float_times @property def st_ctime(self) -> Union[int, float]: """Return the creation time in seconds.""" ctime = self._st_ctime_ns / 1e9 return ctime if self.use_float else int(ctime) @st_ctime.setter def st_ctime(self, val: Union[int, float]) -> None: """Set the creation time in seconds.""" self._st_ctime_ns = int(val * 1e9) @property def st_atime(self) -> Union[int, float]: """Return the access time in seconds.""" atime = self._st_atime_ns / 1e9 return atime if self.use_float else int(atime) @st_atime.setter def st_atime(self, val: Union[int, float]) -> None: """Set the access time in seconds.""" self._st_atime_ns = int(val * 1e9) @property def st_mtime(self) -> Union[int, float]: """Return the modification time in seconds.""" mtime = self._st_mtime_ns / 1e9 return mtime if self.use_float else int(mtime) @st_mtime.setter def st_mtime(self, val: Union[int, float]) -> None: """Set the modification time in seconds.""" self._st_mtime_ns = int(val * 1e9) @property def st_size(self) -> int: if self.st_mode & S_IFLNK == S_IFLNK and self.is_windows: return 0 return self._st_size @st_size.setter def st_size(self, val: int) -> None: self._st_size = val @property def st_file_attributes(self) -> int: if not self.is_windows: raise AttributeError("module 'os.stat_result' " "has no attribute 'st_file_attributes'") mode = 0 st_mode = self.st_mode if st_mode & stat.S_IFDIR: mode |= stat.FILE_ATTRIBUTE_DIRECTORY if st_mode & stat.S_IFREG: mode |= stat.FILE_ATTRIBUTE_NORMAL if st_mode & (stat.S_IFCHR | stat.S_IFBLK): mode |= stat.FILE_ATTRIBUTE_DEVICE if st_mode & stat.S_IFLNK: mode |= stat.FILE_ATTRIBUTE_REPARSE_POINT return mode @property def st_reparse_tag(self) -> int: if not self.is_windows or sys.version_info < (3, 8): raise AttributeError("module 'os.stat_result' " "has no attribute 'st_reparse_tag'") if self.st_mode & stat.S_IFLNK: return stat.IO_REPARSE_TAG_SYMLINK # type: ignore[attr-defined] return 0 def __getitem__(self, item: int) -> Optional[int]: """Implement item access to mimic `os.stat_result` behavior.""" import stat if item == stat.ST_MODE: return self.st_mode if item == stat.ST_INO: return self.st_ino if item == stat.ST_DEV: return self.st_dev if item == stat.ST_NLINK: return self.st_nlink if item == stat.ST_UID: return self.st_uid if item == stat.ST_GID: return self.st_gid if item == stat.ST_SIZE: return self.st_size if item == stat.ST_ATIME: # item access always returns int for backward compatibility return int(self.st_atime) if item == stat.ST_MTIME: return int(self.st_mtime) if item == stat.ST_CTIME: return int(self.st_ctime) raise ValueError('Invalid item') @property def st_atime_ns(self) -> int: """Return the access time in nanoseconds.""" return self._st_atime_ns @st_atime_ns.setter def st_atime_ns(self, val: int) -> None: """Set the access time in nanoseconds.""" self._st_atime_ns = val @property def st_mtime_ns(self) -> int: """Return the modification time in nanoseconds.""" return self._st_mtime_ns @st_mtime_ns.setter def st_mtime_ns(self, val: int) -> None: """Set the modification time of the fake file in nanoseconds.""" self._st_mtime_ns = val @property def st_ctime_ns(self) -> int: """Return the creation time in nanoseconds.""" return self._st_ctime_ns @st_ctime_ns.setter def st_ctime_ns(self, val: int) -> None: """Set the creation time of the fake file in nanoseconds.""" self._st_ctime_ns = val class BinaryBufferIO(io.BytesIO): """Stream class that handles byte contents for files.""" def __init__(self, contents: Optional[bytes]): super().__init__(contents or b'') def putvalue(self, value: bytes) -> None: self.write(value) class TextBufferIO(io.TextIOWrapper): """Stream class that handles Python string contents for files. """ def __init__(self, contents: Optional[bytes] = None, newline: Optional[str] = None, encoding: Optional[str] = None, errors: str = 'strict'): self._bytestream = io.BytesIO(contents or b'') super().__init__(self._bytestream, encoding, errors, newline) def getvalue(self) -> bytes: return self._bytestream.getvalue() def putvalue(self, value: bytes) -> None: self._bytestream.write(value) pyfakefs-4.5.4/pyfakefs/mox3_stubout.py0000666000000000000000000001370513757263357016353 0ustar 00000000000000# Copyright 2008 Google Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ This is a fork of the pymox library intended to work with Python 3. The file was modified by quermit@gmail.com and dawid.fatyga@gmail.com Previously, pyfakefs used just this file from the mox3 library. However, mox3 will soon be decommissioned, yet standard mock cannot be used because of the problem described in pyfakefs #182 and mock issue 250 (https://github.com/testing-cabal/mock/issues/250). Therefore just this file was forked from mox3 and incorporated into pyfakefs. """ import inspect class StubOutForTesting: """Sample Usage: You want os.path.exists() to always return true during testing. stubs = StubOutForTesting() stubs.Set(os.path, 'exists', lambda x: 1) ... stubs.UnsetAll() The above changes os.path.exists into a lambda that returns 1. Once the ... part of the code finishes, the UnsetAll() looks up the old value of os.path.exists and restores it. """ def __init__(self): self.cache = [] self.stubs = [] def __del__(self): self.smart_unset_all() self.unset_all() def smart_set(self, obj, attr_name, new_attr): """Replace obj.attr_name with new_attr. This method is smart and works at the module, class, and instance level while preserving proper inheritance. It will not stub out C types however unless that has been explicitly allowed by the type. This method supports the case where attr_name is a staticmethod or a classmethod of obj. Notes: - If obj is an instance, then it is its class that will actually be stubbed. Note that the method Set() does not do that: if obj is an instance, it (and not its class) will be stubbed. - The stubbing is using the builtin getattr and setattr. So, the __get__ and __set__ will be called when stubbing (TODO: A better idea would probably be to manipulate obj.__dict__ instead of getattr() and setattr()). Raises AttributeError if the attribute cannot be found. """ if (inspect.ismodule(obj) or (not inspect.isclass(obj) and attr_name in obj.__dict__)): orig_obj = obj orig_attr = getattr(obj, attr_name) else: if not inspect.isclass(obj): mro = list(inspect.getmro(obj.__class__)) else: mro = list(inspect.getmro(obj)) mro.reverse() orig_attr = None for cls in mro: try: orig_obj = cls orig_attr = getattr(obj, attr_name) except AttributeError: continue if orig_attr is None: raise AttributeError("Attribute not found.") # Calling getattr() on a staticmethod transforms it to a 'normal' # function. We need to ensure that we put it back as a staticmethod. old_attribute = obj.__dict__.get(attr_name) if (old_attribute is not None and isinstance(old_attribute, staticmethod)): orig_attr = staticmethod(orig_attr) self.stubs.append((orig_obj, attr_name, orig_attr)) setattr(orig_obj, attr_name, new_attr) def smart_unset_all(self): """Reverses all the SmartSet() calls. Restores things to their original definition. Its okay to call SmartUnsetAll() repeatedly, as later calls have no effect if no SmartSet() calls have been made. """ self.stubs.reverse() for args in self.stubs: setattr(*args) self.stubs = [] def set(self, parent, child_name, new_child): """Replace child_name's old definition with new_child. Replace definition in the context of the given parent. The parent could be a module when the child is a function at module scope. Or the parent could be a class when a class' method is being replaced. The named child is set to new_child, while the prior definition is saved away for later, when unset_all() is called. This method supports the case where child_name is a staticmethod or a classmethod of parent. """ old_child = getattr(parent, child_name) old_attribute = parent.__dict__.get(child_name) if old_attribute is not None: if isinstance(old_attribute, staticmethod): old_child = staticmethod(old_child) elif isinstance(old_attribute, classmethod): old_child = classmethod(old_child.__func__) self.cache.append((parent, old_child, child_name)) setattr(parent, child_name, new_child) def unset_all(self): """Reverses all the Set() calls. Restores things to their original definition. Its okay to call unset_all() repeatedly, as later calls have no effect if no Set() calls have been made. """ # Undo calls to set() in reverse order, in case set() was called on the # same arguments repeatedly (want the original call to be last one # undone) self.cache.reverse() for (parent, old_child, child_name) in self.cache: setattr(parent, child_name, old_child) self.cache = [] pyfakefs-4.5.4/pyfakefs/patched_packages.py0000666000000000000000000001023213757263357017136 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Provides patches for some commonly used modules that enable them to work with pyfakefs. """ import sys try: import pandas.io.parsers as parsers except ImportError: parsers = None try: import xlrd except ImportError: xlrd = None try: from django.core.files import locks except ImportError: locks = None def get_modules_to_patch(): modules_to_patch = {} if xlrd is not None: modules_to_patch['xlrd'] = XLRDModule if locks is not None: modules_to_patch['django.core.files.locks'] = FakeLocks return modules_to_patch def get_classes_to_patch(): classes_to_patch = {} if parsers is not None: classes_to_patch[ 'TextFileReader' ] = 'pandas.io.parsers' return classes_to_patch def get_fake_module_classes(): fake_module_classes = {} if parsers is not None: fake_module_classes[ 'TextFileReader' ] = FakeTextFileReader return fake_module_classes if xlrd is not None: class XLRDModule: """Patches the xlrd module, which is used as the default Excel file reader by pandas. Disables using memory mapped files, which are implemented platform-specific on OS level.""" def __init__(self, _): self._xlrd_module = xlrd def open_workbook(self, filename=None, logfile=sys.stdout, verbosity=0, use_mmap=False, file_contents=None, encoding_override=None, formatting_info=False, on_demand=False, ragged_rows=False): return self._xlrd_module.open_workbook( filename, logfile, verbosity, False, file_contents, encoding_override, formatting_info, on_demand, ragged_rows) def __getattr__(self, name): """Forwards any unfaked calls to the standard xlrd module.""" return getattr(self._xlrd_module, name) if parsers is not None: # we currently need to add fake modules for both the parser module and # the contained text reader - maybe this can be simplified class FakeTextFileReader: fake_parsers = None def __init__(self, filesystem): if self.fake_parsers is None: self.__class__.fake_parsers = ParsersModule(filesystem) def __call__(self, *args, **kwargs): return self.fake_parsers.TextFileReader(*args, **kwargs) def __getattr__(self, name): return getattr(self.fake_parsers.TextFileReader, name) class ParsersModule: def __init__(self, _): self._parsers_module = parsers class TextFileReader(parsers.TextFileReader): def __init__(self, *args, **kwargs): kwargs['engine'] = 'python' super().__init__(*args, **kwargs) def __getattr__(self, name): """Forwards any unfaked calls to the standard xlrd module.""" return getattr(self._parsers_module, name) if locks is not None: class FakeLocks: """django.core.files.locks uses low level OS functions, fake it.""" _locks_module = locks def __init__(self, _): pass @staticmethod def lock(f, flags): return True @staticmethod def unlock(f): return True def __getattr__(self, name): return getattr(self._locks_module, name) pyfakefs-4.5.4/pyfakefs/pytest_plugin.py0000666000000000000000000000131714147521400016556 0ustar 00000000000000"""A pytest plugin for using pyfakefs as a fixture When pyfakefs is installed, the "fs" fixture becomes available. :Usage: def my_fakefs_test(fs): fs.create_file('/var/data/xx1.txt') assert os.path.exists('/var/data/xx1.txt') """ import py import pytest from pyfakefs.fake_filesystem_unittest import Patcher Patcher.SKIPMODULES.add(py) Patcher.SKIPMODULES.add(pytest) @pytest.fixture def fs(request): """ Fake filesystem. """ if hasattr(request, 'param'): # pass optional parameters via @pytest.mark.parametrize patcher = Patcher(*request.param) else: patcher = Patcher() patcher.setUp() yield patcher.fs patcher.tearDown() pyfakefs-4.5.4/pyfakefs/pytest_tests/0000777000000000000000000000000014167600566016063 5ustar 00000000000000pyfakefs-4.5.4/pyfakefs/pytest_tests/conftest.py0000666000000000000000000000252414147521400020250 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Example for a custom pytest fixture with an argument to Patcher. # Use this as a template if you want to write your own pytest plugin # with specific Patcher arguments. # See `pytest_plugin.py` for more information. import pytest from pyfakefs.fake_filesystem_unittest import Patcher # import the fs fixture to be visible if pyfakefs is not installed from pyfakefs.pytest_plugin import fs # noqa: F401 from pyfakefs.pytest_tests import example # noqa: E402 @pytest.fixture def fs_reload_example(): """ Fake filesystem. """ patcher = Patcher(modules_to_reload=[example]) patcher.setUp() yield patcher.fs patcher.tearDown() @pytest.fixture def fake_filesystem(fs): # noqa: F811 """Shows how to use an alias for the fs fixture.""" yield fs pyfakefs-4.5.4/pyfakefs/pytest_tests/example.py0000666000000000000000000000123514147521400020054 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Used as SUT for pytest_fixture_test.py from pathlib import Path EXAMPLE_FILE = Path('/test') / 'file' pyfakefs-4.5.4/pyfakefs/pytest_tests/pytest_check_failed_plugin_test.py0000666000000000000000000000127314147521400025031 0ustar 00000000000000"""Tests that a failed pytest properly displays the call stack. Uses the output from running pytest with pytest_plugin_failing_helper.py. Regression test for #381. """ import os import pytest @pytest.mark.skipif(not os.path.exists('testresult.txt'), reason='Only run in CI tests') def test_failed_testresult_stacktrace(): with open('testresult.txt') as f: contents = f.read() # before the fix, a triple question mark has been displayed # instead of the stacktrace assert contents print('contents', contents) assert '???' not in contents assert 'AttributeError' not in contents assert 'def test_fs(fs):' in contents pyfakefs-4.5.4/pyfakefs/pytest_tests/pytest_doctest_test.py0000666000000000000000000000276014147521400022541 0ustar 00000000000000""" This is a test case for pyfakefs issue #45. This problem is resolved by using PyTest version 2.8.6 or above. To run these doctests, install pytest and run: $ pytest --doctest-modules pytest_doctest_test.py Add `-s` option to enable print statements. """ from __future__ import unicode_literals def make_file_factory(func_name, fake, result): """ Return a simple function with parametrized doctest. """ def make_file(name, content=''): with open(name, 'w') as f: f.write(content) make_file.__doc__ = """ >>> import os >>> {command} >>> name, content = 'foo', 'bar' >>> {func_name}(name, content) >>> open(name).read() == content {result} >>> os.remove(name) # Cleanup """.format( command="getfixture('fs')" if fake else "pass", func_name=func_name, result=result) return make_file passes = make_file_factory('passes', fake=False, result=True) passes_too = make_file_factory('passes_too', fake=True, result=True) passes_too.__doc__ = passes_too.__doc__.replace('>>> os.remove(name)', '>>> pass') fails = make_file_factory('fails', fake=False, result=False) # Pytest versions below 2.8.6 raise an internal error when running # these doctests: crashes = make_file_factory('crashes', fake=True, result=False) crashes_too = make_file_factory(') SyntaxError', fake=True, result=False) pyfakefs-4.5.4/pyfakefs/pytest_tests/pytest_fixture_param_test.py0000666000000000000000000000365513757263357023772 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Example for a test using a custom pytest fixture with an argument to Patcher # Needs Python >= 3.6 import os import pytest import pyfakefs.pytest_tests.example as example @pytest.mark.xfail def test_example_file_failing(fs): """Test fails because EXAMPLE_FILE is cached in the module and not patched.""" fs.create_file(example.EXAMPLE_FILE, contents='stuff here') check_that_example_file_is_in_fake_fs() @pytest.mark.parametrize('fs', [[None, [example]]], indirect=True) def test_example_file_passing_using_parametrized_fixture(fs): """Test passes if using a fixture that reloads the module containing EXAMPLE_FILE""" fs.create_file(example.EXAMPLE_FILE, contents='stuff here') check_that_example_file_is_in_fake_fs() def check_that_example_file_is_in_fake_fs(): with open(example.EXAMPLE_FILE) as file: assert file.read() == 'stuff here' with example.EXAMPLE_FILE.open() as file: assert file.read() == 'stuff here' assert example.EXAMPLE_FILE.read_text() == 'stuff here' assert example.EXAMPLE_FILE.is_file() def test_twice_chdir(fs): # regression test for #530 - make sure that # alternative path separators are correctly handled under Windows fs.create_dir("/absolute/path/to/directory") os.chdir("/absolute/path/to/directory") os.chdir("/absolute/path/to/directory") pyfakefs-4.5.4/pyfakefs/pytest_tests/pytest_fixture_test.py0000666000000000000000000000373013757263357022604 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # Example for a test using a custom pytest fixture with an argument to Patcher # Needs Python >= 3.6 import pytest import pyfakefs.pytest_tests.example as example from pyfakefs.fake_filesystem_unittest import Patcher @pytest.mark.xfail def test_example_file_failing(fs): """Test fails because EXAMPLE_FILE is cached in the module and not patched.""" fs.create_file(example.EXAMPLE_FILE, contents='stuff here') check_that_example_file_is_in_fake_fs() def test_example_file_passing_using_fixture(fs_reload_example): """Test passes if using a fixture that reloads the module containing EXAMPLE_FILE""" fs_reload_example.create_file(example.EXAMPLE_FILE, contents='stuff here') check_that_example_file_is_in_fake_fs() def test_example_file_passing_using_patcher(): """Test passes if using a Patcher instance that reloads the module containing EXAMPLE_FILE""" with Patcher(modules_to_reload=[example]) as patcher: patcher.fs.create_file(example.EXAMPLE_FILE, contents='stuff here') check_that_example_file_is_in_fake_fs() def check_that_example_file_is_in_fake_fs(): with open(example.EXAMPLE_FILE) as file: assert file.read() == 'stuff here' with example.EXAMPLE_FILE.open() as file: assert file.read() == 'stuff here' assert example.EXAMPLE_FILE.read_text() == 'stuff here' assert example.EXAMPLE_FILE.is_file() pyfakefs-4.5.4/pyfakefs/pytest_tests/pytest_plugin_failing_helper.py0000666000000000000000000000020614147521400024354 0ustar 00000000000000""" Failing test to test stacktrace output - see ``pytest_check_failed_plugin_test.py``.""" def test_fs(fs): assert False pyfakefs-4.5.4/pyfakefs/pytest_tests/pytest_plugin_test.py0000666000000000000000000000343614147521400022373 0ustar 00000000000000"""Tests that the pytest plugin properly provides the "fs" fixture""" import os import tempfile from pyfakefs.fake_filesystem_unittest import Pause def test_fs_fixture(fs): fs.create_file('/var/data/xx1.txt') assert os.path.exists('/var/data/xx1.txt') def test_fs_fixture_alias(fake_filesystem): fake_filesystem.create_file('/var/data/xx1.txt') assert os.path.exists('/var/data/xx1.txt') def test_both_fixtures(fs, fake_filesystem): fake_filesystem.create_file('/var/data/xx1.txt') fs.create_file('/var/data/xx2.txt') assert os.path.exists('/var/data/xx1.txt') assert os.path.exists('/var/data/xx2.txt') assert fs == fake_filesystem def test_pause_resume(fs): fake_temp_file = tempfile.NamedTemporaryFile() assert fs.exists(fake_temp_file.name) assert os.path.exists(fake_temp_file.name) fs.pause() assert fs.exists(fake_temp_file.name) assert not os.path.exists(fake_temp_file.name) real_temp_file = tempfile.NamedTemporaryFile() assert not fs.exists(real_temp_file.name) assert os.path.exists(real_temp_file.name) fs.resume() assert not os.path.exists(real_temp_file.name) assert os.path.exists(fake_temp_file.name) def test_pause_resume_contextmanager(fs): fake_temp_file = tempfile.NamedTemporaryFile() assert fs.exists(fake_temp_file.name) assert os.path.exists(fake_temp_file.name) with Pause(fs): assert fs.exists(fake_temp_file.name) assert not os.path.exists(fake_temp_file.name) real_temp_file = tempfile.NamedTemporaryFile() assert not fs.exists(real_temp_file.name) assert os.path.exists(real_temp_file.name) assert not os.path.exists(real_temp_file.name) assert os.path.exists(fake_temp_file.name) pyfakefs-4.5.4/pyfakefs/pytest_tests/__init__.py0000666000000000000000000000000013757263357020171 0ustar 00000000000000pyfakefs-4.5.4/pyfakefs/tests/0000777000000000000000000000000014167600566014453 5ustar 00000000000000pyfakefs-4.5.4/pyfakefs/tests/all_tests.py0000666000000000000000000000466314032602703017012 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A test suite that runs all tests for pyfakefs at once. Includes tests with external pathlib2 and scandir packages if installed.""" import sys import unittest from pyfakefs.tests import ( dynamic_patch_test, fake_stat_time_test, example_test, fake_filesystem_glob_test, fake_filesystem_shutil_test, fake_filesystem_test, fake_filesystem_unittest_test, fake_filesystem_vs_real_test, fake_open_test, fake_os_test, fake_pathlib_test, fake_tempfile_test, patched_packages_test, mox3_stubout_test ) class AllTests(unittest.TestSuite): """A test suite that runs all tests for pyfakefs at once.""" def suite(self): # pylint: disable-msg=C6409 loader = unittest.defaultTestLoader self.addTests([ loader.loadTestsFromModule(fake_filesystem_test), loader.loadTestsFromModule(fake_filesystem_glob_test), loader.loadTestsFromModule(fake_filesystem_shutil_test), loader.loadTestsFromModule(fake_os_test), loader.loadTestsFromModule(fake_stat_time_test), loader.loadTestsFromModule(fake_open_test), loader.loadTestsFromModule(fake_tempfile_test), loader.loadTestsFromModule(fake_filesystem_vs_real_test), loader.loadTestsFromModule(fake_filesystem_unittest_test), loader.loadTestsFromModule(example_test), loader.loadTestsFromModule(mox3_stubout_test), loader.loadTestsFromModule(dynamic_patch_test), loader.loadTestsFromModule(fake_pathlib_test), loader.loadTestsFromModule(patched_packages_test) ]) return self if __name__ == '__main__': result = unittest.TextTestRunner(verbosity=2).run(AllTests().suite()) sys.exit(int(not result.wasSuccessful())) pyfakefs-4.5.4/pyfakefs/tests/all_tests_without_extra_packages.py0000666000000000000000000000227614147521400023635 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """A test suite that runs all tests for pyfakefs at once. Excludes tests using external scandir package.""" import sys import unittest from pyfakefs import extra_packages if extra_packages.use_scandir_package: extra_packages.use_scandir_package = False try: from os import scandir except ImportError: scandir = None extra_packages.scandir = scandir extra_packages.use_scandir = scandir from pyfakefs.tests.all_tests import AllTests # noqa: E402 if __name__ == '__main__': result = unittest.TextTestRunner(verbosity=2).run(AllTests().suite()) sys.exit(int(not result.wasSuccessful())) pyfakefs-4.5.4/pyfakefs/tests/dynamic_patch_test.py0000666000000000000000000000416414147521400020657 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Tests for patching modules loaded after `setUpPyfakefs()`. """ import pathlib import unittest from pyfakefs import fake_filesystem_unittest class TestPyfakefsUnittestBase(fake_filesystem_unittest.TestCase): def setUp(self): """Set up the fake file system""" self.setUpPyfakefs() class DynamicImportPatchTest(TestPyfakefsUnittestBase): def __init__(self, methodName='runTest'): super(DynamicImportPatchTest, self).__init__(methodName) def test_os_patch(self): import os os.mkdir('test') self.assertTrue(self.fs.exists('test')) self.assertTrue(os.path.exists('test')) def test_os_import_as_patch(self): import os as _os _os.mkdir('test') self.assertTrue(self.fs.exists('test')) self.assertTrue(_os.path.exists('test')) def test_os_path_patch(self): import os.path os.mkdir('test') self.assertTrue(self.fs.exists('test')) self.assertTrue(os.path.exists('test')) def test_shutil_patch(self): import shutil self.fs.set_disk_usage(100) self.assertEqual(100, shutil.disk_usage('/').total) def test_pathlib_path_patch(self): file_path = 'test.txt' path = pathlib.Path(file_path) with path.open('w') as f: f.write('test') self.assertTrue(self.fs.exists(file_path)) file_object = self.fs.get_object(file_path) self.assertEqual('test', file_object.contents) if __name__ == "__main__": unittest.main() pyfakefs-4.5.4/pyfakefs/tests/example.py0000666000000000000000000001005314147521400016442 0ustar 00000000000000# Copyright 2014 Altera Corporation. All Rights Reserved. # Author: John McGehee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Example module that is tested in :py:class`pyfakefs.example_test.TestExample`. This demonstrates the usage of the :py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class. The modules related to file handling are bound to the respective fake modules: >>> os #doctest: +ELLIPSIS >>> os.path #doctest: +ELLIPSIS >>> shutil #doctest: +ELLIPSIS `open()` is an alias for `io.open()` and is bound to `FakeIoModule.open`. """ import glob import os import shutil try: import scandir has_scandir = True except ImportError: scandir = None has_scandir = False def create_file(path): """Create the specified file and add some content to it. Use the `open()` built in function. For example, the following file operations occur in the fake file system. In the real file system, we would not even have permission to write `/test`: >>> os.path.isdir('/test') False >>> os.mkdir('/test') >>> os.path.isdir('/test') True >>> os.path.exists('/test/file.txt') False >>> create_file('/test/file.txt') >>> os.path.exists('/test/file.txt') True >>> with open('/test/file.txt') as f: ... f.readlines() ["This is test file '/test/file.txt'.\\n", \ 'It was created using open().\\n'] """ with open(path, 'w') as f: f.write("This is test file '{0}'.\n".format(path)) f.write("It was created using open().\n") def delete_file(path): """Delete the specified file. For example: >>> os.mkdir('/test') >>> os.path.exists('/test/file.txt') False >>> create_file('/test/file.txt') >>> os.path.exists('/test/file.txt') True >>> delete_file('/test/file.txt') >>> os.path.exists('/test/file.txt') False """ os.remove(path) def path_exists(path): """Return True if the specified file exists. For example: >>> path_exists('/test') False >>> os.mkdir('/test') >>> path_exists('/test') True >>> >>> path_exists('/test/file.txt') False >>> create_file('/test/file.txt') >>> path_exists('/test/file.txt') True """ return os.path.exists(path) def get_glob(glob_path): r"""Return the list of paths matching the specified glob expression. For example: >>> os.mkdir('/test') >>> create_file('/test/file1.txt') >>> create_file('/test/file2.txt') >>> file_names = sorted(get_glob('/test/file*.txt')) >>> >>> import sys >>> if sys.platform.startswith('win'): ... # Windows style path ... file_names == [r'/test\file1.txt', r'/test\file2.txt'] ... else: ... # UNIX style path ... file_names == ['/test/file1.txt', '/test/file2.txt'] True """ return glob.glob(glob_path) def rm_tree(path): """Delete the specified file hierarchy.""" shutil.rmtree(path) def scan_dir(path): """Return a list of directory entries for the given path.""" if has_scandir: return list(scandir.scandir(path)) return list(os.scandir(path)) def file_contents(path): """Return the contents of the given path as byte array.""" with open(path, 'rb') as f: return f.read() pyfakefs-4.5.4/pyfakefs/tests/example_test.py0000666000000000000000000001623514147521400017511 0ustar 00000000000000# Copyright 2014 Altera Corporation. All Rights Reserved. # Author: John McGehee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Test the :py:class`pyfakefs.example` module to demonstrate the usage of the :py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class. Fake filesystem functions like `create_file()`, `create_dir()` or `create_symlink()` are often used to set up file structures at the beginning of a test. While you could also use the familiar `open()`, `os.mkdirs()` and similar functions, these functions can make the test code shorter and more readable. `create_file()` is particularly convenient because it creates all parent directories and allows you to specify the contents or the size of the file. """ import io import os import sys import unittest from pyfakefs import fake_filesystem_unittest from pyfakefs.extra_packages import use_scandir_package from pyfakefs.tests import example # The module under test def load_tests(loader, tests, ignore): """Load the pyfakefs/example.py doctest tests into unittest.""" return fake_filesystem_unittest.load_doctests( loader, tests, ignore, example) class TestExample(fake_filesystem_unittest.TestCase): # pylint: disable=R0904 """Test the example module. The os and shutil modules have been replaced with the fake modules, so that all of the calls to os and shutil in the tested example code occur in the fake filesystem. """ def setUp(self): """Invoke the :py:class:`pyfakefs.fake_filesystem_unittest.TestCase` `self.setUp()` method. This defines: * Attribute `self.fs`, an instance of :py:class:`pyfakefs.fake_filesystem.FakeFilesystem`. This is useful for creating test files. * Attribute `self.stubs`, an instance of :py:class:`pyfakefs.mox3_stubout.StubOutForTesting`. Use this if you need to define additional stubs. """ # This is before setUpPyfakefs(), so still using the real file system self.filepath = os.path.realpath(__file__) with io.open(self.filepath, 'rb') as f: self.real_contents = f.read() self.setUpPyfakefs() def tearDown(self): # No longer need self.tearDownPyfakefs() pass def test_create_file(self): """Test example.create_file() which uses `open()` and `file.write()`. """ self.assertFalse(os.path.isdir('/test')) os.mkdir('/test') self.assertTrue(os.path.isdir('/test')) self.assertFalse(os.path.exists('/test/file.txt')) example.create_file('/test/file.txt') self.assertTrue(os.path.exists('/test/file.txt')) def test_delete_file(self): """Test example.delete_file() which uses `os.remove()`.""" self.fs.create_file('/test/full.txt', contents='First line\n' 'Second Line\n') self.assertTrue(os.path.exists('/test/full.txt')) example.delete_file('/test/full.txt') self.assertFalse(os.path.exists('/test/full.txt')) def test_file_exists(self): """Test example.path_exists() which uses `os.path.exists()`.""" self.assertFalse(example.path_exists('/test/empty.txt')) self.fs.create_file('/test/empty.txt') self.assertTrue(example.path_exists('/test/empty.txt')) def test_get_globs(self): """Test example.get_glob().""" self.assertFalse(os.path.isdir('/test')) self.fs.create_dir('/test/dir1/dir2a') self.assertTrue(os.path.isdir('/test/dir1/dir2a')) # os.mkdirs() works, too. os.makedirs('/test/dir1/dir2b') self.assertTrue(os.path.isdir('/test/dir1/dir2b')) self.assertEqual(example.get_glob('/test/dir1/nonexistent*'), []) is_windows = sys.platform.startswith('win') matching_paths = sorted(example.get_glob('/test/dir1/dir*')) if is_windows: self.assertEqual(matching_paths, [r'/test/dir1\dir2a', r'/test/dir1\dir2b']) else: self.assertEqual(matching_paths, ['/test/dir1/dir2a', '/test/dir1/dir2b']) def test_rm_tree(self): """Test example.rm_tree() using `shutil.rmtree()`.""" self.fs.create_dir('/test/dir1/dir2a') # os.mkdirs() works, too. os.makedirs('/test/dir1/dir2b') self.assertTrue(os.path.isdir('/test/dir1/dir2b')) self.assertTrue(os.path.isdir('/test/dir1/dir2a')) example.rm_tree('/test/dir1') self.assertFalse(os.path.exists('/test/dir1')) def test_os_scandir(self): """Test example.scandir() which uses `os.scandir()`. The os module has been replaced with the fake os module so the fake filesystem path entries are returned instead of `os.DirEntry` objects. """ self.fs.create_file('/test/text.txt') self.fs.create_dir('/test/dir') self.fs.create_file('/linktest/linked') self.fs.create_symlink('/test/linked_file', '/linktest/linked') entries = sorted(example.scan_dir('/test'), key=lambda e: e.name) self.assertEqual(3, len(entries)) self.assertEqual('linked_file', entries[1].name) self.assertTrue(entries[0].is_dir()) self.assertTrue(entries[1].is_symlink()) self.assertTrue(entries[2].is_file()) @unittest.skipIf(not use_scandir_package, 'Testing only if scandir module is installed') def test_scandir_scandir(self): """Test example.scandir() which uses `scandir.scandir()`. The scandir module has been replaced with the fake_scandir module so the fake filesystem path entries are returned instead of `scandir.DirEntry` objects. """ self.fs.create_file('/test/text.txt') self.fs.create_dir('/test/dir') entries = sorted(example.scan_dir('/test'), key=lambda e: e.name) self.assertEqual(2, len(entries)) self.assertEqual('text.txt', entries[1].name) self.assertTrue(entries[0].is_dir()) self.assertTrue(entries[1].is_file()) def test_real_file_access(self): """Test `example.file_contents()` for a real file after adding it using `add_real_file()`.""" with self.assertRaises(OSError): example.file_contents(self.filepath) self.fs.add_real_file(self.filepath) self.assertEqual(example.file_contents(self.filepath), self.real_contents) if __name__ == "__main__": unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_filesystem_glob_test.py0000666000000000000000000000507314147521400022231 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Test for glob using fake_filesystem.""" import glob import os import unittest from pyfakefs import fake_filesystem_unittest class FakeGlobUnitTest(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs() directory = './xyzzy' self.fs.create_dir(directory) self.fs.create_dir('%s/subdir' % directory) self.fs.create_dir('%s/subdir2' % directory) self.fs.create_file('%s/subfile' % directory) self.fs.create_file('[Temp]') def test_glob_empty(self): self.assertEqual(glob.glob(''), []) def test_glob_star(self): basedir = '/xyzzy' self.assertEqual([os.path.join(basedir, 'subdir'), os.path.join(basedir, 'subdir2'), os.path.join(basedir, 'subfile')], sorted(glob.glob('/xyzzy/*'))) def test_glob_exact(self): self.assertEqual(['/xyzzy'], glob.glob('/xyzzy')) self.assertEqual(['/xyzzy/subfile'], glob.glob('/xyzzy/subfile')) def test_glob_question(self): basedir = '/xyzzy' self.assertEqual([os.path.join(basedir, 'subdir'), os.path.join(basedir, 'subdir2'), os.path.join(basedir, 'subfile')], sorted(glob.glob('/x?zz?/*'))) def test_glob_no_magic(self): self.assertEqual(['/xyzzy'], glob.glob('/xyzzy')) self.assertEqual(['/xyzzy/subdir'], glob.glob('/xyzzy/subdir')) def test_non_existent_path(self): self.assertEqual([], glob.glob('nonexistent')) def test_magic_dir(self): self.assertEqual(['/[Temp]'], glob.glob('/*emp*')) def test_glob1(self): self.assertEqual(['[Temp]'], glob.glob1('/', '*Tem*')) def test_has_magic(self): self.assertTrue(glob.has_magic('[')) self.assertFalse(glob.has_magic('a')) if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_filesystem_shutil_test.py0000666000000000000000000005261214147521400022617 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests for `fake_filesystem_shutil` if used in `fake_filesystem_unittest.TestCase`. Note that almost all of the functionality is delegated to the real `shutil` and works correctly with the fake filesystem because of the faked `os` module. """ import os import shutil import sys import tempfile import unittest from pyfakefs import fake_filesystem_unittest from pyfakefs.fake_filesystem import is_root, set_uid, USER_ID from pyfakefs.tests.test_utils import RealFsTestMixin is_windows = sys.platform == 'win32' class RealFsTestCase(fake_filesystem_unittest.TestCase, RealFsTestMixin): def __init__(self, methodName='runTest'): fake_filesystem_unittest.TestCase.__init__(self, methodName) RealFsTestMixin.__init__(self) def setUp(self): RealFsTestMixin.setUp(self) self.cwd = os.getcwd() self.uid = USER_ID set_uid(1000) if not self.use_real_fs(): self.setUpPyfakefs() self.filesystem = self.fs self.os = os self.open = open self.create_basepath() self.fs.set_disk_usage(1000, self.base_path) def tearDown(self): set_uid(self.uid) RealFsTestMixin.tearDown(self) @property def is_windows_fs(self): if self.use_real_fs(): return sys.platform == 'win32' return self.filesystem.is_windows_fs class FakeShutilModuleTest(RealFsTestCase): @unittest.skipIf(is_windows, 'Posix specific behavior') def test_catch_permission_error(self): root_path = self.make_path('rootpath') self.create_dir(root_path) dir1_path = self.os.path.join(root_path, 'dir1') dir2_path = self.os.path.join(root_path, 'dir2') self.create_dir(dir1_path) self.os.chmod(dir1_path, 0o555) # remove write permissions self.create_dir(dir2_path) old_file_path = self.os.path.join(dir2_path, 'f1.txt') new_file_path = self.os.path.join(dir1_path, 'f1.txt') self.create_file(old_file_path) with self.assertRaises(PermissionError): shutil.move(old_file_path, new_file_path) def test_rmtree(self): directory = self.make_path('xyzzy') dir_path = os.path.join(directory, 'subdir') self.create_dir(dir_path) file_path = os.path.join(directory, 'subfile') self.create_file(file_path) self.assertTrue(os.path.exists(directory)) shutil.rmtree(directory) self.assertFalse(os.path.exists(directory)) self.assertFalse(os.path.exists(dir_path)) self.assertFalse(os.path.exists(file_path)) def test_rmtree_with_trailing_slash(self): directory = self.make_path('xyzzy') dir_path = os.path.join(directory, 'subdir') self.create_dir(dir_path) file_path = os.path.join(directory, 'subfile') self.create_file(file_path) shutil.rmtree(directory + '/') self.assertFalse(os.path.exists(directory)) self.assertFalse(os.path.exists(dir_path)) self.assertFalse(os.path.exists(file_path)) @unittest.skipIf(not is_windows, 'Windows specific behavior') def test_rmtree_without_permission_for_a_file_in_windows(self): self.check_windows_only() dir_path = self.make_path('foo') self.create_file(os.path.join(dir_path, 'bar')) file_path = os.path.join(dir_path, 'baz') self.create_file(file_path) self.os.chmod(file_path, 0o444) with self.assertRaises(OSError): shutil.rmtree(dir_path) self.assertTrue(os.path.exists(file_path)) self.os.chmod(file_path, 0o666) @unittest.skipIf(is_windows, 'Posix specific behavior') def test_rmtree_without_permission_for_a_dir_in_posix(self): self.check_posix_only() dir_path = self.make_path('foo') self.create_file(os.path.join(dir_path, 'bar')) file_path = os.path.join(dir_path, 'baz') self.create_file(file_path) self.os.chmod(dir_path, 0o555) if not is_root(): with self.assertRaises(OSError): shutil.rmtree(dir_path) self.assertTrue(os.path.exists(file_path)) self.os.chmod(dir_path, 0o777) else: shutil.rmtree(dir_path) self.assertFalse(os.path.exists(file_path)) @unittest.skipIf(is_windows, 'Posix specific behavior') def test_rmtree_with_open_file_posix(self): self.check_posix_only() dir_path = self.make_path('foo') self.create_file(os.path.join(dir_path, 'bar')) file_path = os.path.join(dir_path, 'baz') self.create_file(file_path) with open(file_path): shutil.rmtree(dir_path) self.assertFalse(os.path.exists(file_path)) @unittest.skipIf(not is_windows, 'Windows specific behavior') def test_rmtree_with_open_file_fails_under_windows(self): self.check_windows_only() dir_path = self.make_path('foo') self.create_file(os.path.join(dir_path, 'bar')) file_path = os.path.join(dir_path, 'baz') self.create_file(file_path) with open(file_path): with self.assertRaises(OSError): shutil.rmtree(dir_path) self.assertTrue(os.path.exists(dir_path)) def test_rmtree_non_existing_dir(self): directory = 'nonexisting' with self.assertRaises(OSError): shutil.rmtree(directory) try: shutil.rmtree(directory, ignore_errors=True) except OSError: self.fail('rmtree raised despite ignore_errors True') def test_rmtree_non_existing_dir_with_handler(self): class NonLocal: pass def error_handler(_, path, _error_info): NonLocal.errorHandled = True NonLocal.errorPath = path directory = self.make_path('nonexisting') NonLocal.errorHandled = False NonLocal.errorPath = '' try: shutil.rmtree(directory, onerror=error_handler) except OSError: self.fail('rmtree raised exception despite onerror defined') self.assertTrue(NonLocal.errorHandled) self.assertEqual(NonLocal.errorPath, directory) NonLocal.errorHandled = False NonLocal.errorPath = '' try: shutil.rmtree(directory, ignore_errors=True, onerror=error_handler) except OSError: self.fail('rmtree raised exception despite ignore_errors True') # ignore_errors is True, so the onerror() error handler was # not executed self.assertFalse(NonLocal.errorHandled) self.assertEqual(NonLocal.errorPath, '') def test_copy(self): src_file = self.make_path('xyzzy') dst_file = self.make_path('xyzzy_copy') self.create_file(src_file) os.chmod(src_file, 0o750) self.assertTrue(os.path.exists(src_file)) self.assertFalse(os.path.exists(dst_file)) shutil.copy(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) self.assertEqual(os.stat(src_file).st_mode, os.stat(dst_file).st_mode) def test_copy_directory(self): src_file = self.make_path('xyzzy') parent_directory = self.make_path('parent') dst_file = os.path.join(parent_directory, 'xyzzy') self.create_file(src_file) self.create_dir(parent_directory) os.chmod(src_file, 0o750) self.assertTrue(os.path.exists(src_file)) self.assertTrue(os.path.exists(parent_directory)) self.assertFalse(os.path.exists(dst_file)) shutil.copy(src_file, parent_directory) self.assertTrue(os.path.exists(dst_file)) self.assertEqual(os.stat(src_file).st_mode, os.stat(dst_file).st_mode) def test_copystat(self): src_file = self.make_path('xyzzy') self.create_file(src_file) os.chmod(src_file, 0o750) dst_file = self.make_path('xyzzy_copy') self.create_file(dst_file) self.assertTrue(os.path.exists(src_file)) self.assertTrue(os.path.exists(dst_file)) shutil.copystat(src_file, dst_file) src_stat = os.stat(src_file) dst_stat = os.stat(dst_file) self.assertEqual(src_stat.st_mode, dst_stat.st_mode) self.assertAlmostEqual(src_stat.st_atime, dst_stat.st_atime, places=2) self.assertAlmostEqual(src_stat.st_mtime, dst_stat.st_mtime, places=2) def test_copy2(self): src_file = self.make_path('xyzzy') self.create_file(src_file) os.chmod(src_file, 0o750) dst_file = self.make_path('xyzzy_copy') self.assertTrue(os.path.exists(src_file)) self.assertFalse(os.path.exists(dst_file)) shutil.copy2(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) src_stat = os.stat(src_file) dst_stat = os.stat(dst_file) self.assertEqual(src_stat.st_mode, dst_stat.st_mode) self.assertAlmostEqual(src_stat.st_atime, dst_stat.st_atime, places=2) self.assertAlmostEqual(src_stat.st_mtime, dst_stat.st_mtime, places=2) def test_copy2_directory(self): src_file = self.make_path('xyzzy') parent_directory = self.make_path('parent') dst_file = os.path.join(parent_directory, 'xyzzy') self.create_file(src_file) self.create_dir(parent_directory) os.chmod(src_file, 0o750) self.assertTrue(os.path.exists(src_file)) self.assertTrue(os.path.exists(parent_directory)) self.assertFalse(os.path.exists(dst_file)) shutil.copy2(src_file, parent_directory) self.assertTrue(os.path.exists(dst_file)) src_stat = os.stat(src_file) dst_stat = os.stat(dst_file) self.assertEqual(src_stat.st_mode, dst_stat.st_mode) self.assertAlmostEqual(src_stat.st_atime, dst_stat.st_atime, places=2) self.assertAlmostEqual(src_stat.st_mtime, dst_stat.st_mtime, places=2) def test_copytree(self): src_directory = self.make_path('xyzzy') dst_directory = self.make_path('xyzzy_copy') self.create_dir(src_directory) self.create_dir('%s/subdir' % src_directory) self.create_file(os.path.join(src_directory, 'subfile')) self.assertTrue(os.path.exists(src_directory)) self.assertFalse(os.path.exists(dst_directory)) shutil.copytree(src_directory, dst_directory) self.assertTrue(os.path.exists(dst_directory)) self.assertTrue(os.path.exists(os.path.join(dst_directory, 'subdir'))) self.assertTrue(os.path.exists(os.path.join(dst_directory, 'subfile'))) def test_copytree_src_is_file(self): src_file = self.make_path('xyzzy') dst_directory = self.make_path('xyzzy_copy') self.create_file(src_file) self.assertTrue(os.path.exists(src_file)) self.assertFalse(os.path.exists(dst_directory)) with self.assertRaises(OSError): shutil.copytree(src_file, dst_directory) def test_move_file_in_same_filesystem(self): self.skip_real_fs() src_file = '/original_xyzzy' dst_file = '/moved_xyzzy' src_object = self.fs.create_file(src_file) src_ino = src_object.st_ino src_dev = src_object.st_dev self.assertTrue(os.path.exists(src_file)) self.assertFalse(os.path.exists(dst_file)) shutil.move(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) self.assertFalse(os.path.exists(src_file)) dst_object = self.fs.get_object(dst_file) self.assertEqual(src_ino, dst_object.st_ino) self.assertEqual(src_dev, dst_object.st_dev) def test_move_file_into_other_filesystem(self): self.skip_real_fs() mount_point = self.create_mount_point() src_file = self.make_path('original_xyzzy') dst_file = self.os.path.join(mount_point, 'moved_xyzzy') src_object = self.fs.create_file(src_file) src_ino = src_object.st_ino src_dev = src_object.st_dev shutil.move(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) self.assertFalse(os.path.exists(src_file)) dst_object = self.fs.get_object(dst_file) self.assertNotEqual(src_ino, dst_object.st_ino) self.assertNotEqual(src_dev, dst_object.st_dev) def test_move_file_into_directory(self): src_file = self.make_path('xyzzy') dst_directory = self.make_path('directory') dst_file = os.path.join(dst_directory, 'xyzzy') self.create_file(src_file) self.create_dir(dst_directory) self.assertTrue(os.path.exists(src_file)) self.assertFalse(os.path.exists(dst_file)) shutil.move(src_file, dst_directory) self.assertTrue(os.path.exists(dst_file)) self.assertFalse(os.path.exists(src_file)) def test_move_directory(self): src_directory = self.make_path('original_xyzzy') dst_directory = self.make_path('moved_xyzzy') self.create_dir(src_directory) self.create_file(os.path.join(src_directory, 'subfile')) self.create_dir(os.path.join(src_directory, 'subdir')) self.assertTrue(os.path.exists(src_directory)) self.assertFalse(os.path.exists(dst_directory)) shutil.move(src_directory, dst_directory) self.assertTrue(os.path.exists(dst_directory)) self.assertTrue(os.path.exists(os.path.join(dst_directory, 'subfile'))) self.assertTrue(os.path.exists(os.path.join(dst_directory, 'subdir'))) self.assertFalse(os.path.exists(src_directory)) def test_disk_usage(self): self.skip_real_fs() file_path = self.make_path('foo', 'bar') self.fs.create_file(file_path, st_size=400) # root = self.os.path.splitdrive(file_path)[0] + self.fs.path_separator disk_usage = shutil.disk_usage(file_path) self.assertEqual(1000, disk_usage.total) self.assertEqual(400, disk_usage.used) self.assertEqual(600, disk_usage.free) self.assertEqual((1000, 400, 600), disk_usage) mount_point = self.create_mount_point() dir_path = self.os.path.join(mount_point, 'foo') file_path = self.os.path.join(dir_path, 'bar') self.fs.create_file(file_path, st_size=400) disk_usage = shutil.disk_usage(dir_path) self.assertEqual((500, 400, 100), disk_usage) def create_mount_point(self): mount_point = 'M:' if self.is_windows_fs else '/mount' self.fs.add_mount_point(mount_point, total_size=500) return mount_point class RealShutilModuleTest(FakeShutilModuleTest): def use_real_fs(self): return True class FakeCopyFileTest(RealFsTestCase): def tearDown(self): super(FakeCopyFileTest, self).tearDown() def test_common_case(self): src_file = self.make_path('xyzzy') dst_file = self.make_path('xyzzy_copy') contents = 'contents of file' self.create_file(src_file, contents=contents) self.assertTrue(os.path.exists(src_file)) self.assertFalse(os.path.exists(dst_file)) shutil.copyfile(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) self.check_contents(dst_file, contents) def test_raises_if_source_and_dest_are_the_same_file(self): src_file = self.make_path('xyzzy') dst_file = src_file contents = 'contents of file' self.create_file(src_file, contents=contents) self.assertTrue(os.path.exists(src_file)) with self.assertRaises(shutil.Error): shutil.copyfile(src_file, dst_file) def test_raises_if_dest_is_a_symlink_to_src(self): self.skip_if_symlink_not_supported() src_file = self.make_path('foo') dst_file = self.make_path('bar') contents = 'contents of file' self.create_file(src_file, contents=contents) self.create_symlink(dst_file, src_file) self.assertTrue(os.path.exists(src_file)) with self.assertRaises(shutil.Error): shutil.copyfile(src_file, dst_file) def test_succeeds_if_dest_exists_and_is_writable(self): src_file = self.make_path('xyzzy') dst_file = self.make_path('xyzzy_copy') src_contents = 'contents of source file' dst_contents = 'contents of dest file' self.create_file(src_file, contents=src_contents) self.create_file(dst_file, contents=dst_contents) self.assertTrue(os.path.exists(src_file)) self.assertTrue(os.path.exists(dst_file)) shutil.copyfile(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) self.check_contents(dst_file, src_contents) def test_raises_if_dest_exists_and_is_not_writable(self): src_file = self.make_path('xyzzy') dst_file = self.make_path('xyzzy_copy') src_contents = 'contents of source file' dst_contents = 'contents of dest file' self.create_file(src_file, contents=src_contents) self.create_file(dst_file, contents=dst_contents) os.chmod(dst_file, 0o400) self.assertTrue(os.path.exists(src_file)) self.assertTrue(os.path.exists(dst_file)) if is_root(): shutil.copyfile(src_file, dst_file) self.assertTrue(self.os.path.exists(dst_file)) with self.open(dst_file) as f: self.assertEqual('contents of source file', f.read()) else: with self.assertRaises(OSError): shutil.copyfile(src_file, dst_file) os.chmod(dst_file, 0o666) @unittest.skipIf(is_windows, 'Posix specific behavior') def test_raises_if_dest_dir_is_not_writable_under_posix(self): self.check_posix_only() src_file = self.make_path('xyzzy') dst_dir = self.make_path('tmp', 'foo') dst_file = os.path.join(dst_dir, 'xyzzy') src_contents = 'contents of source file' self.create_file(src_file, contents=src_contents) self.create_dir(dst_dir) os.chmod(dst_dir, 0o555) self.assertTrue(os.path.exists(src_file)) self.assertTrue(os.path.exists(dst_dir)) if not is_root(): with self.assertRaises(OSError): shutil.copyfile(src_file, dst_file) else: shutil.copyfile(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) self.check_contents(dst_file, src_contents) def test_raises_if_src_doesnt_exist(self): src_file = self.make_path('xyzzy') dst_file = self.make_path('xyzzy_copy') self.assertFalse(os.path.exists(src_file)) with self.assertRaises(OSError): shutil.copyfile(src_file, dst_file) @unittest.skipIf(is_windows, 'Posix specific behavior') def test_raises_if_src_not_readable(self): self.check_posix_only() src_file = self.make_path('xyzzy') dst_file = self.make_path('xyzzy_copy') src_contents = 'contents of source file' self.create_file(src_file, contents=src_contents) os.chmod(src_file, 0o000) self.assertTrue(os.path.exists(src_file)) if not is_root(): with self.assertRaises(OSError): shutil.copyfile(src_file, dst_file) else: shutil.copyfile(src_file, dst_file) self.assertTrue(os.path.exists(dst_file)) self.check_contents(dst_file, src_contents) def test_raises_if_src_is_a_directory(self): src_file = self.make_path('xyzzy') dst_file = self.make_path('xyzzy_copy') self.create_dir(src_file) self.assertTrue(os.path.exists(src_file)) with self.assertRaises(OSError): shutil.copyfile(src_file, dst_file) def test_raises_if_dest_is_a_directory(self): src_file = self.make_path('xyzzy') dst_dir = self.make_path('tmp', 'foo') src_contents = 'contents of source file' self.create_file(src_file, contents=src_contents) self.create_dir(dst_dir) self.assertTrue(os.path.exists(src_file)) self.assertTrue(os.path.exists(dst_dir)) with self.assertRaises(OSError): shutil.copyfile(src_file, dst_dir) def test_moving_dir_into_dir(self): # regression test for #515 source_dir = tempfile.mkdtemp() target_dir = tempfile.mkdtemp() filename = 'foo.pdf' with open(os.path.join(source_dir, filename), 'wb') as fp: fp.write(b'stub') shutil.move(source_dir, target_dir) shutil.rmtree(target_dir) class RealCopyFileTest(FakeCopyFileTest): def use_real_fs(self): return True if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_filesystem_test.py0000666000000000000000000031310114147521400021220 0ustar 00000000000000# -*- coding: utf-8 -*- # Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unittest for fake_filesystem module.""" import contextlib import errno import os import stat import sys import unittest from pyfakefs import fake_filesystem from pyfakefs.fake_filesystem import set_uid, set_gid, is_root, reset_ids from pyfakefs.helpers import IS_WIN from pyfakefs.tests.test_utils import TestCase, RealFsTestCase, time_mock class FakeDirectoryUnitTest(TestCase): def setUp(self): self.time = time_mock(10, 1) self.time.start() self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.os = fake_filesystem.FakeOsModule(self.filesystem) self.fake_file = fake_filesystem.FakeFile( 'foobar', contents='dummy_file', filesystem=self.filesystem) self.fake_dir = fake_filesystem.FakeDirectory( 'somedir', filesystem=self.filesystem) def tearDown(self): self.time.stop() def test_new_file_and_directory(self): self.assertTrue(stat.S_IFREG & self.fake_file.st_mode) self.assertTrue(stat.S_IFDIR & self.fake_dir.st_mode) self.assertEqual({}, self.fake_dir.entries) self.assertEqual(12, self.fake_file.st_ctime) def test_add_entry(self): self.fake_dir.add_entry(self.fake_file) self.assertEqual({'foobar': self.fake_file}, self.fake_dir.entries) def test_get_entry(self): self.fake_dir.add_entry(self.fake_file) self.assertEqual(self.fake_file, self.fake_dir.get_entry('foobar')) def test_path(self): self.filesystem.root.add_entry(self.fake_dir) self.fake_dir.add_entry(self.fake_file) self.assertEqual('/somedir/foobar', self.fake_file.path) self.assertEqual('/somedir', self.fake_dir.path) def test_path_with_drive(self): self.filesystem.is_windows_fs = True dir_path = 'C:/foo/bar/baz' self.filesystem.create_dir(dir_path) dir_object = self.filesystem.get_object(dir_path) self.assertEqual(dir_path, dir_object.path) def test_path_after_chdir(self): dir_path = '/foo/bar/baz' self.filesystem.create_dir(dir_path) self.os.chdir(dir_path) dir_object = self.filesystem.get_object(dir_path) self.assertEqual(dir_path, dir_object.path) def test_path_after_chdir_with_drive(self): self.filesystem.is_windows_fs = True dir_path = 'C:/foo/bar/baz' self.filesystem.create_dir(dir_path) self.os.chdir(dir_path) dir_object = self.filesystem.get_object(dir_path) self.assertEqual(dir_path, dir_object.path) def test_remove_entry(self): self.fake_dir.add_entry(self.fake_file) self.assertEqual(self.fake_file, self.fake_dir.get_entry('foobar')) self.fake_dir.remove_entry('foobar') with self.assertRaises(KeyError): self.fake_dir.get_entry('foobar') def test_should_throw_if_set_size_is_not_integer(self): def set_size(): self.fake_file.size = 0.1 self.assert_raises_os_error(errno.ENOSPC, set_size) def test_should_throw_if_set_size_is_negative(self): def set_size(): self.fake_file.size = -1 self.assert_raises_os_error(errno.ENOSPC, set_size) def test_produce_empty_file_if_set_size_is_zero(self): self.fake_file.size = 0 self.assertEqual('', self.fake_file.contents) def test_sets_content_empty_if_set_size_is_zero(self): self.fake_file.size = 0 self.assertEqual('', self.fake_file.contents) def test_truncate_file_if_size_is_smaller_than_current_size(self): self.fake_file.size = 6 self.assertEqual('dummy_', self.fake_file.contents) def test_leave_file_unchanged_if_size_is_equal_to_current_size(self): self.fake_file.size = 10 self.assertEqual('dummy_file', self.fake_file.contents) def test_set_contents_to_dir_raises(self): # Regression test for #276 self.filesystem.is_windows_fs = True self.assert_raises_os_error( errno.EISDIR, self.fake_dir.set_contents, 'a') self.filesystem.is_windows_fs = False self.assert_raises_os_error( errno.EISDIR, self.fake_dir.set_contents, 'a') def test_pads_with_nullbytes_if_size_is_greater_than_current_size(self): self.fake_file.size = 13 self.assertEqual('dummy_file\0\0\0', self.fake_file.contents) def test_set_m_time(self): self.assertEqual(12, self.fake_file.st_mtime) self.fake_file.st_mtime = 13 self.assertEqual(13, self.fake_file.st_mtime) self.fake_file.st_mtime = 131 self.assertEqual(131, self.fake_file.st_mtime) def test_file_inode(self): filesystem = fake_filesystem.FakeFilesystem(path_separator='/') fake_os = fake_filesystem.FakeOsModule(filesystem) file_path = 'some_file1' filesystem.create_file(file_path, contents='contents here1') self.assertLess(0, fake_os.stat(file_path)[stat.ST_INO]) file_obj = filesystem.get_object(file_path) file_obj.st_ino = 43 self.assertEqual(43, fake_os.stat(file_path)[stat.ST_INO]) def test_directory_inode(self): filesystem = fake_filesystem.FakeFilesystem(path_separator='/') fake_os = fake_filesystem.FakeOsModule(filesystem) dirpath = 'testdir' filesystem.create_dir(dirpath) self.assertLess(0, fake_os.stat(dirpath)[stat.ST_INO]) dir_obj = filesystem.get_object(dirpath) dir_obj.st_ino = 43 self.assertEqual(43, fake_os.stat(dirpath)[stat.ST_INO]) def test_ordered_dirs(self): filesystem = fake_filesystem.FakeFilesystem(path_separator='/') filesystem.create_dir('/foo') filesystem.create_file('/foo/2') filesystem.create_file('/foo/4') filesystem.create_file('/foo/1') filesystem.create_file('/foo/3') fake_dir = filesystem.get_object('/foo') self.assertEqual(['2', '4', '1', '3'], fake_dir.ordered_dirs) class SetLargeFileSizeTest(TestCase): def setUp(self): filesystem = fake_filesystem.FakeFilesystem() self.fake_file = fake_filesystem.FakeFile('foobar', filesystem=filesystem) def test_should_throw_if_size_is_not_integer(self): self.assert_raises_os_error(errno.ENOSPC, self.fake_file.set_large_file_size, 0.1) def test_should_throw_if_size_is_negative(self): self.assert_raises_os_error(errno.ENOSPC, self.fake_file.set_large_file_size, -1) def test_sets_content_none_if_size_is_non_negative_integer(self): self.fake_file.set_large_file_size(1000000000) self.assertEqual(None, self.fake_file.contents) self.assertEqual(1000000000, self.fake_file.st_size) class NormalizePathTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.root_name = '/' def test_empty_path_should_get_normalized_to_root_path(self): self.assertEqual(self.root_name, self.filesystem.absnormpath('')) def test_root_path_remains_unchanged(self): self.assertEqual(self.root_name, self.filesystem.absnormpath(self.root_name)) def test_relative_path_forced_to_cwd(self): path = 'bar' self.filesystem.cwd = '/foo' self.assertEqual('/foo/bar', self.filesystem.absnormpath(path)) def test_absolute_path_remains_unchanged(self): path = '/foo/bar' self.assertEqual(path, self.filesystem.absnormpath(path)) def test_dotted_path_is_normalized(self): path = '/foo/..' self.assertEqual('/', self.filesystem.absnormpath(path)) path = 'foo/../bar' self.assertEqual('/bar', self.filesystem.absnormpath(path)) def test_dot_path_is_normalized(self): path = '.' self.assertEqual('/', self.filesystem.absnormpath(path)) class GetPathComponentsTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.root_name = '/' def test_root_path_should_return_empty_list(self): self.assertEqual([], self.filesystem._path_components(self.root_name)) def test_empty_path_should_return_empty_list(self): self.assertEqual([], self.filesystem._path_components('')) def test_relative_path_with_one_component_should_return_component(self): self.assertEqual(['foo'], self.filesystem._path_components('foo')) def test_absolute_path_with_one_component_should_return_component(self): self.assertEqual(['foo'], self.filesystem._path_components('/foo')) def test_two_level_relative_path_should_return_components(self): self.assertEqual(['foo', 'bar'], self.filesystem._path_components('foo/bar')) def test_two_level_absolute_path_should_return_components(self): self.assertEqual(['foo', 'bar'], self.filesystem._path_components('/foo/bar')) class FakeFilesystemUnitTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.root_name = '/' self.fake_file = fake_filesystem.FakeFile( 'foobar', filesystem=self.filesystem) self.fake_child = fake_filesystem.FakeDirectory( 'foobaz', filesystem=self.filesystem) self.fake_grandchild = fake_filesystem.FakeDirectory( 'quux', filesystem=self.filesystem) def test_new_filesystem(self): self.assertEqual('/', self.filesystem.path_separator) self.assertTrue(stat.S_IFDIR & self.filesystem.root.st_mode) self.assertEqual(self.root_name, self.filesystem.root.name) self.assertEqual({}, self.filesystem.root.entries) def test_none_raises_type_error(self): with self.assertRaises(TypeError): self.filesystem.exists(None) def test_empty_string_does_not_exist(self): self.assertFalse(self.filesystem.exists('')) def test_exists_root(self): self.assertTrue(self.filesystem.exists(self.root_name)) def test_exists_unadded_file(self): self.assertFalse(self.filesystem.exists(self.fake_file.name)) def test_not_exists_subpath_named_like_file_contents(self): # Regression test for #219 file_path = "/foo/bar" self.filesystem.create_file(file_path, contents='baz') self.assertFalse(self.filesystem.exists(file_path + "/baz")) def test_get_root_object(self): self.assertEqual(self.filesystem.root, self.filesystem.get_object(self.root_name)) def test_add_object_to_root(self): self.filesystem.add_object(self.root_name, self.fake_file) self.assertEqual({'foobar': self.fake_file}, self.filesystem.root.entries) def test_exists_added_file(self): self.filesystem.add_object(self.root_name, self.fake_file) self.assertTrue(self.filesystem.exists(self.fake_file.name)) def test_exists_relative_path_posix(self): self.filesystem.is_windows_fs = False self.filesystem.create_file('/a/b/file_one') self.filesystem.create_file('/a/c/file_two') self.assertTrue(self.filesystem.exists('a/b/../c/file_two')) self.assertTrue(self.filesystem.exists('/a/c/../b/file_one')) self.assertTrue(self.filesystem.exists('/a/c/../../a/b/file_one')) self.assertFalse(self.filesystem.exists('a/b/../z/d')) self.assertFalse(self.filesystem.exists('a/b/../z/../c/file_two')) self.filesystem.cwd = '/a/c' self.assertTrue(self.filesystem.exists('../b/file_one')) self.assertTrue(self.filesystem.exists('../../a/b/file_one')) self.assertTrue(self.filesystem.exists('../../a/b/../../a/c/file_two')) self.assertFalse(self.filesystem.exists('../z/file_one')) self.assertFalse(self.filesystem.exists('../z/../c/file_two')) def test_exists_relative_path_windows(self): self.filesystem.is_windows_fs = True self.filesystem.is_macos = False self.filesystem.create_file('/a/b/file_one') self.filesystem.create_file('/a/c/file_two') self.assertTrue(self.filesystem.exists('a/b/../c/file_two')) self.assertTrue(self.filesystem.exists('/a/c/../b/file_one')) self.assertTrue(self.filesystem.exists('/a/c/../../a/b/file_one')) self.assertFalse(self.filesystem.exists('a/b/../z/d')) self.assertTrue(self.filesystem.exists('a/b/../z/../c/file_two')) self.filesystem.cwd = '/a/c' self.assertTrue(self.filesystem.exists('../b/file_one')) self.assertTrue(self.filesystem.exists('../../a/b/file_one')) self.assertTrue(self.filesystem.exists('../../a/b/../../a/c/file_two')) self.assertFalse(self.filesystem.exists('../z/file_one')) self.assertTrue(self.filesystem.exists('../z/../c/file_two')) def test_get_object_from_root(self): self.filesystem.add_object(self.root_name, self.fake_file) self.assertEqual(self.fake_file, self.filesystem.get_object('foobar')) def test_get_nonexistent_object_from_root_error(self): self.filesystem.add_object(self.root_name, self.fake_file) self.assertEqual(self.fake_file, self.filesystem.get_object('foobar')) self.assert_raises_os_error( errno.ENOENT, self.filesystem.get_object, 'some_bogus_filename') def test_remove_object_from_root(self): self.filesystem.add_object(self.root_name, self.fake_file) self.filesystem.remove_object(self.fake_file.name) self.assert_raises_os_error( errno.ENOENT, self.filesystem.get_object, self.fake_file.name) def test_remove_nonexisten_object_from_root_error(self): self.assert_raises_os_error( errno.ENOENT, self.filesystem.remove_object, 'some_bogus_filename') def test_exists_removed_file(self): self.filesystem.add_object(self.root_name, self.fake_file) self.filesystem.remove_object(self.fake_file.name) self.assertFalse(self.filesystem.exists(self.fake_file.name)) def test_add_object_to_child(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_file) self.assertEqual( {self.fake_file.name: self.fake_file}, self.filesystem.root.get_entry(self.fake_child.name).entries) def test_add_object_to_regular_file_error_posix(self): self.filesystem.is_windows_fs = False self.filesystem.add_object(self.root_name, self.fake_file) self.assert_raises_os_error(errno.ENOTDIR, self.filesystem.add_object, self.fake_file.name, self.fake_file) def test_add_object_to_regular_file_error_windows(self): self.filesystem.is_windows_fs = True self.filesystem.add_object(self.root_name, self.fake_file) self.assert_raises_os_error(errno.ENOENT, self.filesystem.add_object, self.fake_file.name, self.fake_file) def test_exists_file_added_to_child(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_file) path = self.filesystem.joinpaths(self.fake_child.name, self.fake_file.name) self.assertTrue(self.filesystem.exists(path)) def test_get_object_from_child(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_file) self.assertEqual(self.fake_file, self.filesystem.get_object( self.filesystem.joinpaths(self.fake_child.name, self.fake_file.name))) def test_get_nonexistent_object_from_child_error(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_file) self.assert_raises_os_error(errno.ENOENT, self.filesystem.get_object, self.filesystem.joinpaths( self.fake_child.name, 'some_bogus_filename')) def test_remove_object_from_child(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_file) target_path = self.filesystem.joinpaths(self.fake_child.name, self.fake_file.name) self.filesystem.remove_object(target_path) self.assert_raises_os_error(errno.ENOENT, self.filesystem.get_object, target_path) def test_remove_object_from_child_error(self): self.filesystem.add_object(self.root_name, self.fake_child) self.assert_raises_os_error( errno.ENOENT, self.filesystem.remove_object, self.filesystem.joinpaths(self.fake_child.name, 'some_bogus_filename')) def test_remove_object_from_non_directory_error(self): self.filesystem.add_object(self.root_name, self.fake_file) self.assert_raises_os_error( errno.ENOTDIR, self.filesystem.remove_object, self.filesystem.joinpaths( '%s' % self.fake_file.name, 'file_does_not_matter_since_parent_not_a_directory')) def test_exists_file_removed_from_child(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_file) path = self.filesystem.joinpaths(self.fake_child.name, self.fake_file.name) self.filesystem.remove_object(path) self.assertFalse(self.filesystem.exists(path)) def test_operate_on_grandchild_directory(self): self.filesystem.add_object(self.root_name, self.fake_child) self.filesystem.add_object(self.fake_child.name, self.fake_grandchild) grandchild_directory = self.filesystem.joinpaths( self.fake_child.name, self.fake_grandchild.name) grandchild_file = self.filesystem.joinpaths( grandchild_directory, self.fake_file.name) with self.assertRaises(OSError): self.filesystem.get_object(grandchild_file) self.filesystem.add_object(grandchild_directory, self.fake_file) self.assertEqual(self.fake_file, self.filesystem.get_object(grandchild_file)) self.assertTrue(self.filesystem.exists(grandchild_file)) self.filesystem.remove_object(grandchild_file) with self.assertRaises(OSError): self.filesystem.get_object(grandchild_file) self.assertFalse(self.filesystem.exists(grandchild_file)) def test_create_directory_in_root_directory(self): path = 'foo' self.filesystem.create_dir(path) new_dir = self.filesystem.get_object(path) self.assertEqual(os.path.basename(path), new_dir.name) self.assertTrue(stat.S_IFDIR & new_dir.st_mode) def test_create_directory_in_root_directory_already_exists_error(self): path = 'foo' self.filesystem.create_dir(path) self.assert_raises_os_error( errno.EEXIST, self.filesystem.create_dir, path) def test_create_directory(self): path = 'foo/bar/baz' self.filesystem.create_dir(path) new_dir = self.filesystem.get_object(path) self.assertEqual(os.path.basename(path), new_dir.name) self.assertTrue(stat.S_IFDIR & new_dir.st_mode) # Create second directory to make sure first is OK. path = '%s/quux' % path self.filesystem.create_dir(path) new_dir = self.filesystem.get_object(path) self.assertEqual(os.path.basename(path), new_dir.name) self.assertTrue(stat.S_IFDIR & new_dir.st_mode) def test_create_directory_already_exists_error(self): path = 'foo/bar/baz' self.filesystem.create_dir(path) self.assert_raises_os_error( errno.EEXIST, self.filesystem.create_dir, path) def test_create_file_in_read_only_directory_raises_in_posix(self): self.filesystem.is_windows_fs = False dir_path = '/foo/bar' self.filesystem.create_dir(dir_path, perm_bits=0o555) file_path = dir_path + '/baz' if not is_root(): self.assert_raises_os_error(errno.EACCES, self.filesystem.create_file, file_path) else: self.filesystem.create_file(file_path) self.assertTrue(self.filesystem.exists(file_path)) def test_create_file_in_read_only_directory_possible_in_windows(self): self.filesystem.is_windows_fs = True dir_path = 'C:/foo/bar' self.filesystem.create_dir(dir_path, perm_bits=0o555) file_path = dir_path + '/baz' self.filesystem.create_file(file_path) self.assertTrue(self.filesystem.exists(file_path)) def test_create_file_in_current_directory(self): path = 'foo' contents = 'dummy data' self.filesystem.create_file(path, contents=contents) self.assertTrue(self.filesystem.exists(path)) self.assertFalse(self.filesystem.exists(os.path.dirname(path))) path = './%s' % path self.assertTrue(self.filesystem.exists(os.path.dirname(path))) def test_create_file_in_root_directory(self): path = '/foo' contents = 'dummy data' self.filesystem.create_file(path, contents=contents) new_file = self.filesystem.get_object(path) self.assertTrue(self.filesystem.exists(path)) self.assertTrue(self.filesystem.exists(os.path.dirname(path))) self.assertEqual(os.path.basename(path), new_file.name) self.assertTrue(stat.S_IFREG & new_file.st_mode) self.assertEqual(contents, new_file.contents) def test_create_file_with_size_but_no_content_creates_large_file(self): path = 'large_foo_bar' self.filesystem.create_file(path, st_size=100000000) new_file = self.filesystem.get_object(path) self.assertEqual(None, new_file.contents) self.assertEqual(100000000, new_file.st_size) def test_create_file_in_root_directory_already_exists_error(self): path = 'foo' self.filesystem.create_file(path) self.assert_raises_os_error( errno.EEXIST, self.filesystem.create_file, path) def test_create_file(self): path = 'foo/bar/baz' retval = self.filesystem.create_file(path, contents='dummy_data') self.assertTrue(self.filesystem.exists(path)) self.assertTrue(self.filesystem.exists(os.path.dirname(path))) new_file = self.filesystem.get_object(path) self.assertEqual(os.path.basename(path), new_file.name) if IS_WIN: self.assertEqual(1, new_file.st_uid) self.assertEqual(1, new_file.st_gid) else: self.assertEqual(os.getuid(), new_file.st_uid) self.assertEqual(os.getgid(), new_file.st_gid) self.assertEqual(new_file, retval) def test_create_file_with_changed_ids(self): path = 'foo/bar/baz' set_uid(42) set_gid(2) self.filesystem.create_file(path) self.assertTrue(self.filesystem.exists(path)) new_file = self.filesystem.get_object(path) self.assertEqual(42, new_file.st_uid) self.assertEqual(2, new_file.st_gid) reset_ids() def test_empty_file_created_for_none_contents(self): fake_open = fake_filesystem.FakeFileOpen(self.filesystem) path = 'foo/bar/baz' self.filesystem.create_file(path, contents=None) with fake_open(path) as f: self.assertEqual('', f.read()) def test_create_file_with_incorrect_mode_type(self): with self.assertRaises(TypeError): self.filesystem.create_file('foo', 'bar') def test_create_file_already_exists_error(self): path = 'foo/bar/baz' self.filesystem.create_file(path, contents='dummy_data') self.assert_raises_os_error( errno.EEXIST, self.filesystem.create_file, path) def test_create_link(self): path = 'foo/bar/baz' target_path = 'foo/bar/quux' new_file = self.filesystem.create_symlink(path, 'quux') # Neither the path nor the final target exists before we actually # write to one of them, even though the link appears in the file # system. self.assertFalse(self.filesystem.exists(path)) self.assertFalse(self.filesystem.exists(target_path)) self.assertTrue(stat.S_IFLNK & new_file.st_mode) # but once we write the linked to file, they both will exist. self.filesystem.create_file(target_path) self.assertTrue(self.filesystem.exists(path)) self.assertTrue(self.filesystem.exists(target_path)) def test_resolve_object(self): target_path = 'dir/target' target_contents = '0123456789ABCDEF' link_name = 'x' self.filesystem.create_dir('dir') self.filesystem.create_file('dir/target', contents=target_contents) self.filesystem.create_symlink(link_name, target_path) obj = self.filesystem.resolve(link_name) self.assertEqual('target', obj.name) self.assertEqual(target_contents, obj.contents) def check_lresolve_object(self): target_path = 'dir/target' target_contents = '0123456789ABCDEF' link_name = 'x' self.filesystem.create_dir('dir') self.filesystem.create_file('dir/target', contents=target_contents) self.filesystem.create_symlink(link_name, target_path) obj = self.filesystem.lresolve(link_name) self.assertEqual(link_name, obj.name) self.assertEqual(target_path, obj.contents) def test_lresolve_object_windows(self): self.filesystem.is_windows_fs = True self.check_lresolve_object() def test_lresolve_object_posix(self): self.filesystem.is_windows_fs = False self.check_lresolve_object() def check_directory_access_on_file(self, error_subtype): self.filesystem.create_file('not_a_dir') self.assert_raises_os_error( error_subtype, self.filesystem.resolve, 'not_a_dir/foo') self.assert_raises_os_error( error_subtype, self.filesystem.lresolve, 'not_a_dir/foo/bar') def test_directory_access_on_file_windows(self): self.filesystem.is_windows_fs = True self.check_directory_access_on_file(errno.ENOENT) def test_directory_access_on_file_posix(self): self.filesystem.is_windows_fs = False self.check_directory_access_on_file(errno.ENOTDIR) def test_pickle_fs(self): """Regression test for #445""" import pickle self.filesystem.open_files = [] p = pickle.dumps(self.filesystem) fs = pickle.loads(p) self.assertEqual(str(fs.root), str(self.filesystem.root)) self.assertEqual(fs.mount_points, self.filesystem.mount_points) class CaseInsensitiveFakeFilesystemTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.filesystem.is_case_sensitive = False self.os = fake_filesystem.FakeOsModule(self.filesystem) self.path = self.os.path def test_get_object(self): self.filesystem.create_dir('/foo/bar') self.filesystem.create_file('/foo/bar/baz') self.assertTrue(self.filesystem.get_object('/Foo/Bar/Baz')) def test_remove_object(self): self.filesystem.create_dir('/foo/bar') self.filesystem.create_file('/foo/bar/baz') self.filesystem.remove_object('/Foo/Bar/Baz') self.assertFalse(self.filesystem.exists('/foo/bar/baz')) def test_exists(self): self.filesystem.create_dir('/Foo/Bar') self.assertTrue(self.filesystem.exists('/Foo/Bar')) self.assertTrue(self.filesystem.exists('/foo/bar')) self.filesystem.create_file('/foo/Bar/baz') self.assertTrue(self.filesystem.exists('/Foo/bar/BAZ')) self.assertTrue(self.filesystem.exists('/foo/bar/baz')) def test_create_directory_with_different_case_root(self): self.filesystem.create_dir('/Foo/Bar') self.filesystem.create_dir('/foo/bar/baz') dir1 = self.filesystem.get_object('/Foo/Bar') dir2 = self.filesystem.get_object('/foo/bar') self.assertEqual(dir1, dir2) def test_create_file_with_different_case_dir(self): self.filesystem.create_dir('/Foo/Bar') self.filesystem.create_file('/foo/bar/baz') dir1 = self.filesystem.get_object('/Foo/Bar') dir2 = self.filesystem.get_object('/foo/bar') self.assertEqual(dir1, dir2) def test_resolve_path(self): self.filesystem.create_dir('/foo/baz') self.filesystem.create_symlink('/Foo/Bar', './baz/bip') self.assertEqual('/foo/baz/bip', self.filesystem.resolve_path('/foo/bar')) def test_isdir_isfile(self): self.filesystem.create_file('foo/bar') self.assertTrue(self.path.isdir('Foo')) self.assertFalse(self.path.isfile('Foo')) self.assertTrue(self.path.isfile('Foo/Bar')) self.assertFalse(self.path.isdir('Foo/Bar')) def test_getsize(self): file_path = 'foo/bar/baz' self.filesystem.create_file(file_path, contents='1234567') self.assertEqual(7, self.path.getsize('FOO/BAR/BAZ')) def test_getsize_with_looping_symlink(self): self.filesystem.is_windows_fs = False dir_path = '/foo/bar' self.filesystem.create_dir(dir_path) link_path = dir_path + "/link" link_target = link_path + "/link" self.os.symlink(link_target, link_path) self.assert_raises_os_error( errno.ELOOP, self.os.path.getsize, link_path) def test_get_mtime(self): test_file = self.filesystem.create_file('foo/bar1.txt') test_file.st_mtime = 24 self.assertEqual(24, self.path.getmtime('Foo/Bar1.TXT')) def test_get_object_with_file_size(self): self.filesystem.create_file('/Foo/Bar', st_size=10) self.assertTrue(self.filesystem.get_object('/foo/bar')) class CaseSensitiveFakeFilesystemTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.filesystem.is_case_sensitive = True self.os = fake_filesystem.FakeOsModule(self.filesystem) self.path = self.os.path def test_get_object(self): self.filesystem.create_dir('/foo/bar') self.filesystem.create_file('/foo/bar/baz') with self.assertRaises(OSError): self.filesystem.get_object('/Foo/Bar/Baz') def test_remove_object(self): self.filesystem.create_dir('/foo/bar') self.filesystem.create_file('/foo/bar/baz') with self.assertRaises(OSError): self.filesystem.remove_object('/Foo/Bar/Baz') self.assertTrue(self.filesystem.exists('/foo/bar/baz')) def test_exists(self): self.filesystem.create_dir('/Foo/Bar') self.assertTrue(self.filesystem.exists('/Foo/Bar')) self.assertFalse(self.filesystem.exists('/foo/bar')) self.filesystem.create_file('/foo/Bar/baz') self.assertFalse(self.filesystem.exists('/Foo/bar/BAZ')) self.assertFalse(self.filesystem.exists('/foo/bar/baz')) def test_create_directory_with_different_case_root(self): self.filesystem.create_dir('/Foo/Bar') self.filesystem.create_dir('/foo/bar/baz') dir1 = self.filesystem.get_object('/Foo/Bar') dir2 = self.filesystem.get_object('/foo/bar') self.assertNotEqual(dir1, dir2) def test_create_file_with_different_case_dir(self): self.filesystem.create_dir('/Foo/Bar') self.filesystem.create_file('/foo/bar/baz') dir1 = self.filesystem.get_object('/Foo/Bar') dir2 = self.filesystem.get_object('/foo/bar') self.assertNotEqual(dir1, dir2) def test_isdir_isfile(self): self.filesystem.create_file('foo/bar') self.assertFalse(self.path.isdir('Foo')) self.assertFalse(self.path.isfile('Foo')) self.assertFalse(self.path.isfile('Foo/Bar')) self.assertFalse(self.path.isdir('Foo/Bar')) def test_getsize(self): file_path = 'foo/bar/baz' self.filesystem.create_file(file_path, contents='1234567') with self.assertRaises(os.error): self.path.getsize('FOO/BAR/BAZ') def test_get_mtime(self): test_file = self.filesystem.create_file('foo/bar1.txt') test_file.st_mtime = 24 self.assert_raises_os_error( errno.ENOENT, self.path.getmtime, 'Foo/Bar1.TXT') class OsPathInjectionRegressionTest(TestCase): """Test faking os.path before calling os.walk. Found when investigating a problem with gws/tools/labrat/rat_utils_unittest, which was faking out os.path before calling os.walk. """ def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.os_path = os.path # The bug was that when os.path gets faked, the FakePathModule doesn't # get called in self.os.walk(). FakePathModule now insists that it is # created as part of FakeOsModule. self.os = fake_filesystem.FakeOsModule(self.filesystem) def tearDown(self): os.path = self.os_path def test_create_top_level_directory(self): top_level_dir = '/x' self.assertFalse(self.filesystem.exists(top_level_dir)) self.filesystem.create_dir(top_level_dir) self.assertTrue(self.filesystem.exists('/')) self.assertTrue(self.filesystem.exists(top_level_dir)) self.filesystem.create_dir('%s/po' % top_level_dir) self.filesystem.create_file('%s/po/control' % top_level_dir) self.filesystem.create_file('%s/po/experiment' % top_level_dir) self.filesystem.create_dir('%s/gv' % top_level_dir) self.filesystem.create_file('%s/gv/control' % top_level_dir) expected = [ ('/', ['x'], []), ('/x', ['gv', 'po'], []), ('/x/gv', [], ['control']), ('/x/po', [], ['control', 'experiment']), ] # as the result is unsorted, we have to check against sorted results result = sorted([step for step in self.os.walk('/')], key=lambda l: l[0]) self.assertEqual(len(expected), len(result)) for entry, expected_entry in zip(result, expected): self.assertEqual(expected_entry[0], entry[0]) self.assertEqual(expected_entry[1], sorted(entry[1])) self.assertEqual(expected_entry[2], sorted(entry[2])) class FakePathModuleTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!') self.os = fake_filesystem.FakeOsModule(self.filesystem) self.path = self.os.path def check_abspath(self, is_windows): # the implementation differs in Windows and Posix, so test both self.filesystem.is_windows_fs = is_windows filename = 'foo' abspath = '!%s' % filename self.filesystem.create_file(abspath) self.assertEqual(abspath, self.path.abspath(abspath)) self.assertEqual(abspath, self.path.abspath(filename)) self.assertEqual(abspath, self.path.abspath('..!%s' % filename)) def test_abspath_windows(self): self.check_abspath(is_windows=True) def test_abspath_posix(self): """abspath should return a consistent representation of a file.""" self.check_abspath(is_windows=False) def check_abspath_bytes(self, is_windows): """abspath should return a consistent representation of a file.""" self.filesystem.is_windows_fs = is_windows filename = b'foo' abspath = b'!' + filename self.filesystem.create_file(abspath) self.assertEqual(abspath, self.path.abspath(abspath)) self.assertEqual(abspath, self.path.abspath(filename)) self.assertEqual(abspath, self.path.abspath(b'..!' + filename)) def test_abspath_bytes_windows(self): self.check_abspath_bytes(is_windows=True) def test_abspath_bytes_posix(self): self.check_abspath_bytes(is_windows=False) def test_abspath_deals_with_relative_non_root_path(self): """abspath should correctly handle relative paths from a non-! directory. This test is distinct from the basic functionality test because fake_filesystem has historically been based in !. """ filename = '!foo!bar!baz' file_components = filename.split(self.path.sep) basedir = '!%s' % (file_components[0],) self.filesystem.create_file(filename) self.os.chdir(basedir) self.assertEqual(basedir, self.path.abspath(self.path.curdir)) self.assertEqual('!', self.path.abspath('..')) self.assertEqual(self.path.join(basedir, file_components[1]), self.path.abspath(file_components[1])) def test_abs_path_with_drive_component(self): self.filesystem.is_windows_fs = True self.filesystem.cwd = 'C:!foo' self.assertEqual('C:!foo!bar', self.path.abspath('bar')) self.assertEqual('C:!foo!bar', self.path.abspath('C:bar')) self.assertEqual('C:!foo!bar', self.path.abspath('!foo!bar')) def test_isabs_with_drive_component(self): self.filesystem.is_windows_fs = False self.assertFalse(self.path.isabs('C:!foo')) self.assertFalse(self.path.isabs(b'C:!foo')) self.assertTrue(self.path.isabs('!')) self.assertTrue(self.path.isabs(b'!')) self.filesystem.is_windows_fs = True self.assertTrue(self.path.isabs('C:!foo')) self.assertTrue(self.path.isabs(b'C:!foo')) self.assertTrue(self.path.isabs('!')) self.assertTrue(self.path.isabs(b'!')) def test_relpath(self): path_foo = '!path!to!foo' path_bar = '!path!to!bar' path_other = '!some!where!else' with self.assertRaises(ValueError): self.path.relpath(None) with self.assertRaises(ValueError): self.path.relpath('') self.assertEqual('path!to!foo', self.path.relpath(path_foo)) self.assertEqual('..!foo', self.path.relpath(path_foo, path_bar)) self.assertEqual('..!..!..%s' % path_other, self.path.relpath(path_other, path_bar)) self.assertEqual('.', self.path.relpath(path_bar, path_bar)) def test_realpath_vs_abspath(self): self.filesystem.is_windows_fs = False self.filesystem.create_file('!george!washington!bridge') self.filesystem.create_symlink('!first!president', '!george!washington') self.assertEqual('!first!president!bridge', self.os.path.abspath('!first!president!bridge')) self.assertEqual('!george!washington!bridge', self.os.path.realpath('!first!president!bridge')) self.os.chdir('!first!president') self.assertEqual('!george!washington!bridge', self.os.path.realpath('bridge')) @unittest.skipIf(sys.version_info < (3, 10), "'strict' new in Python 3.10") def test_realpath_strict(self): self.filesystem.create_file('!foo!bar') self.filesystem.cwd = '!foo' self.assertEqual('!foo!baz', self.os.path.realpath('baz', strict=False)) self.assert_raises_os_error(errno.ENOENT, self.os.path.realpath, 'baz', strict=True) self.assertEqual('!foo!bar', self.os.path.realpath('bar', strict=True)) def test_samefile(self): file_path1 = '!foo!bar!baz' file_path2 = '!foo!bar!boo' self.filesystem.create_file(file_path1) self.filesystem.create_file(file_path2) self.assertTrue(self.path.samefile(file_path1, file_path1)) self.assertFalse(self.path.samefile(file_path1, file_path2)) self.assertTrue( self.path.samefile(file_path1, '!foo!..!foo!bar!..!bar!baz')) self.assertTrue( self.path.samefile(file_path1, b'!foo!..!foo!bar!..!bar!baz')) def test_exists(self): file_path = 'foo!bar!baz' file_path_bytes = b'foo!bar!baz' self.filesystem.create_file(file_path) self.assertTrue(self.path.exists(file_path)) self.assertTrue(self.path.exists(file_path_bytes)) self.assertFalse(self.path.exists('!some!other!bogus!path')) def test_lexists(self): file_path = 'foo!bar!baz' file_path_bytes = b'foo!bar!baz' self.filesystem.create_dir('foo!bar') self.filesystem.create_symlink(file_path, 'bogus') self.assertTrue(self.path.lexists(file_path)) self.assertTrue(self.path.lexists(file_path_bytes)) self.assertFalse(self.path.exists(file_path)) self.assertFalse(self.path.exists(file_path_bytes)) self.filesystem.create_file('foo!bar!bogus') self.assertTrue(self.path.exists(file_path)) def test_dirname_with_drive(self): self.filesystem.is_windows_fs = True self.assertEqual('c:!foo', self.path.dirname('c:!foo!bar')) self.assertEqual(b'c:!', self.path.dirname(b'c:!foo')) self.assertEqual('!foo', self.path.dirname('!foo!bar')) self.assertEqual(b'!', self.path.dirname(b'!foo')) self.assertEqual('c:foo', self.path.dirname('c:foo!bar')) self.assertEqual(b'c:', self.path.dirname(b'c:foo')) self.assertEqual('foo', self.path.dirname('foo!bar')) def test_dirname(self): dirname = 'foo!bar' self.assertEqual(dirname, self.path.dirname('%s!baz' % dirname)) def test_join_strings(self): components = ['foo', 'bar', 'baz'] self.assertEqual('foo!bar!baz', self.path.join(*components)) def test_join_bytes(self): components = [b'foo', b'bar', b'baz'] self.assertEqual(b'foo!bar!baz', self.path.join(*components)) def test_expand_user(self): if self.is_windows: self.assertEqual(self.path.expanduser('~'), self.os.environ['USERPROFILE'].replace('\\', '!')) else: self.assertEqual(self.path.expanduser('~'), self.os.environ['HOME'].replace('/', '!')) @unittest.skipIf(TestCase.is_windows or TestCase.is_cygwin, 'only tested on unix systems') def test_expand_root(self): if sys.platform == 'darwin': roothome = '!var!root' else: roothome = '!root' self.assertEqual(self.path.expanduser('~root'), roothome) def test_getsize_path_nonexistent(self): file_path = 'foo!bar!baz' with self.assertRaises(os.error): self.path.getsize(file_path) def test_getsize_file_empty(self): file_path = 'foo!bar!baz' self.filesystem.create_file(file_path) self.assertEqual(0, self.path.getsize(file_path)) def test_getsize_file_non_zero_size(self): file_path = 'foo!bar!baz' file_path_bytes = b'foo!bar!baz' self.filesystem.create_file(file_path, contents='1234567') self.assertEqual(7, self.path.getsize(file_path)) self.assertEqual(7, self.path.getsize(file_path_bytes)) def test_getsize_dir_empty(self): # For directories, only require that the size is non-negative. dir_path = 'foo!bar' self.filesystem.create_dir(dir_path) size = self.path.getsize(dir_path) self.assertFalse(int(size) < 0, 'expected non-negative size; actual: %s' % size) def test_getsize_dir_non_zero_size(self): # For directories, only require that the size is non-negative. dir_path = 'foo!bar' self.filesystem.create_file(self.filesystem.joinpaths(dir_path, 'baz')) size = self.path.getsize(dir_path) self.assertFalse(int(size) < 0, 'expected non-negative size; actual: %s' % size) def test_isdir(self): self.filesystem.create_file('foo!bar') self.assertTrue(self.path.isdir('foo')) self.assertTrue(self.path.isdir(b'foo')) self.assertFalse(self.path.isdir('foo!bar')) self.assertFalse(self.path.isdir('it_dont_exist')) def test_isdir_with_cwd_change(self): self.filesystem.create_file('!foo!bar!baz') self.assertTrue(self.path.isdir('!foo')) self.assertTrue(self.path.isdir('!foo!bar')) self.assertTrue(self.path.isdir('foo')) self.assertTrue(self.path.isdir('foo!bar')) self.filesystem.cwd = '!foo' self.assertTrue(self.path.isdir('!foo')) self.assertTrue(self.path.isdir('!foo!bar')) self.assertTrue(self.path.isdir('bar')) def test_isfile(self): self.filesystem.create_file('foo!bar') self.assertFalse(self.path.isfile('foo')) self.assertTrue(self.path.isfile('foo!bar')) self.assertTrue(self.path.isfile(b'foo!bar')) self.assertFalse(self.path.isfile('it_dont_exist')) def test_get_mtime(self): test_file = self.filesystem.create_file('foo!bar1.txt') self.assertNotEqual(24, self.path.getmtime('foo!bar1.txt')) test_file.st_mtime = 24 self.assertEqual(24, self.path.getmtime('foo!bar1.txt')) self.assertEqual(24, self.path.getmtime(b'foo!bar1.txt')) def test_get_mtime_raises_os_error(self): self.assertFalse(self.path.exists('it_dont_exist')) self.assert_raises_os_error(errno.ENOENT, self.path.getmtime, 'it_dont_exist') def test_islink(self): self.filesystem.create_dir('foo') self.filesystem.create_file('foo!regular_file') self.filesystem.create_symlink('foo!link_to_file', 'regular_file') self.assertFalse(self.path.islink('foo')) # An object can be both a link and a file or file, according to the # comments in Python/Lib/posixpath.py. self.assertTrue(self.path.islink('foo!link_to_file')) self.assertTrue(self.path.isfile('foo!link_to_file')) self.assertTrue(self.path.islink(b'foo!link_to_file')) self.assertTrue(self.path.isfile(b'foo!link_to_file')) self.assertTrue(self.path.isfile('foo!regular_file')) self.assertFalse(self.path.islink('foo!regular_file')) self.assertFalse(self.path.islink('it_dont_exist')) def test_is_link_case_sensitive(self): # Regression test for #306 self.filesystem.is_case_sensitive = False self.filesystem.create_dir('foo') self.filesystem.create_symlink('foo!bar', 'foo') self.assertTrue(self.path.islink('foo!Bar')) def test_ismount(self): self.assertFalse(self.path.ismount('')) self.assertTrue(self.path.ismount('!')) self.assertTrue(self.path.ismount(b'!')) self.assertFalse(self.path.ismount('!mount!')) self.filesystem.add_mount_point('!mount') self.assertTrue(self.path.ismount('!mount')) self.assertTrue(self.path.ismount(b'!mount')) self.assertTrue(self.path.ismount('!mount!')) def test_ismount_with_drive_letters(self): self.filesystem.is_windows_fs = True self.assertTrue(self.path.ismount('!')) self.assertTrue(self.path.ismount('c:!')) self.assertFalse(self.path.ismount('c:')) self.assertTrue(self.path.ismount('z:!')) self.filesystem.add_mount_point('!mount') self.assertTrue(self.path.ismount('!mount')) self.assertTrue(self.path.ismount('!mount!')) def test_ismount_with_unc_paths(self): self.filesystem.is_windows_fs = True self.assertTrue(self.path.ismount('!!a!')) self.assertTrue(self.path.ismount('!!a!b')) self.assertTrue(self.path.ismount('!!a!b!')) self.assertFalse(self.path.ismount('!a!b!')) self.assertFalse(self.path.ismount('!!a!b!c')) def test_ismount_with_alternate_path_separator(self): self.filesystem.alternative_path_separator = '!' self.filesystem.add_mount_point('!mount') self.assertTrue(self.path.ismount('!mount')) self.assertTrue(self.path.ismount('!mount!')) self.assertTrue(self.path.ismount('!mount!!')) self.filesystem.is_windows_fs = True self.assertTrue(self.path.ismount('Z:!')) def test_getattr_forward_to_real_os_path(self): """Forwards any non-faked calls to os.path.""" self.assertTrue(hasattr(self.path, 'sep'), 'Get a faked os.path function') private_path_function = None if sys.version_info < (3, 6): if self.is_windows: private_path_function = '_get_bothseps' else: private_path_function = '_join_real_path' if private_path_function: self.assertTrue(hasattr(self.path, private_path_function), 'Get a real os.path function ' 'not implemented in fake os.path') self.assertFalse(hasattr(self.path, 'nonexistent')) class PathManipulationTestBase(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='|') class CollapsePathPipeSeparatorTest(PathManipulationTestBase): """Tests CollapsePath (mimics os.path.normpath) using | as path separator.""" def test_empty_path_becomes_dot_path(self): self.assertEqual('.', self.filesystem.normpath('')) def test_dot_path_unchanged(self): self.assertEqual('.', self.filesystem.normpath('.')) def test_slashes_are_not_collapsed(self): """Tests that '/' is not treated specially if the path separator is '|'. In particular, multiple slashes should not be collapsed. """ self.assertEqual('/', self.filesystem.normpath('/')) self.assertEqual('/////', self.filesystem.normpath('/////')) def test_root_path(self): self.assertEqual('|', self.filesystem.normpath('|')) def test_multiple_separators_collapsed_into_root_path(self): self.assertEqual('|', self.filesystem.normpath('|||||')) def test_all_dot_paths_removed_but_one(self): self.assertEqual('.', self.filesystem.normpath('.|.|.|.')) def test_all_dot_paths_removed_if_another_path_component_exists(self): self.assertEqual('|', self.filesystem.normpath('|.|.|.|')) self.assertEqual('foo|bar', self.filesystem.normpath('foo|.|.|.|bar')) def test_ignores_up_level_references_starting_from_root(self): self.assertEqual('|', self.filesystem.normpath('|..|..|..|')) self.assertEqual( '|', self.filesystem.normpath('|..|..|foo|bar|..|..|')) self.filesystem.is_windows_fs = False # not an UNC path self.assertEqual('|', self.filesystem.normpath('||..|.|..||')) def test_conserves_up_level_references_starting_from_current_dir(self): self.assertEqual( '..|..', self.filesystem.normpath('..|foo|bar|..|..|..')) def test_combine_dot_and_up_level_references_in_absolute_path(self): self.assertEqual( '|yes', self.filesystem.normpath('|||||.|..|||yes|no|..|.|||')) def test_dots_in_path_collapses_to_last_path(self): self.assertEqual( 'bar', self.filesystem.normpath('foo|..|bar')) self.assertEqual( 'bar', self.filesystem.normpath('foo|..|yes|..|no|..|bar')) class SplitPathTest(PathManipulationTestBase): """Tests SplitPath (which mimics os.path.split) using | as path separator.""" def test_empty_path(self): self.assertEqual(('', ''), self.filesystem.splitpath('')) def test_no_separators(self): self.assertEqual(('', 'ab'), self.filesystem.splitpath('ab')) def test_slashes_do_not_split(self): """Tests that '/' is not treated specially if the path separator is '|'.""" self.assertEqual(('', 'a/b'), self.filesystem.splitpath('a/b')) def test_eliminate_trailing_separators_from_head(self): self.assertEqual(('a', 'b'), self.filesystem.splitpath('a|b')) self.assertEqual(('a', 'b'), self.filesystem.splitpath('a|||b')) self.assertEqual(('|a', 'b'), self.filesystem.splitpath('|a||b')) self.assertEqual(('a|b', 'c'), self.filesystem.splitpath('a|b|c')) self.assertEqual(('|a|b', 'c'), self.filesystem.splitpath('|a|b|c')) def test_root_separator_is_not_stripped(self): self.assertEqual(('|||', ''), self.filesystem.splitpath('|||')) self.assertEqual(('|', 'a'), self.filesystem.splitpath('|a')) self.assertEqual(('|||', 'a'), self.filesystem.splitpath('|||a')) def test_empty_tail_if_path_ends_in_separator(self): self.assertEqual(('a|b', ''), self.filesystem.splitpath('a|b|')) def test_empty_path_components_are_preserved_in_head(self): self.assertEqual(('|a||b', 'c'), self.filesystem.splitpath('|a||b||c')) class JoinPathTest(PathManipulationTestBase): """Tests JoinPath (which mimics os.path.join) using | as path separator.""" def test_one_empty_component(self): self.assertEqual('', self.filesystem.joinpaths('')) def test_multiple_empty_components(self): self.assertEqual('', self.filesystem.joinpaths('', '', '')) def test_separators_not_stripped_from_single_component(self): self.assertEqual('||a||', self.filesystem.joinpaths('||a||')) def test_one_separator_added_between_components(self): self.assertEqual('a|b|c|d', self.filesystem.joinpaths('a', 'b', 'c', 'd')) def test_no_separator_added_for_components_ending_in_separator(self): self.assertEqual('a|b|c', self.filesystem.joinpaths('a|', 'b|', 'c')) self.assertEqual('a|||b|||c', self.filesystem.joinpaths('a|||', 'b|||', 'c')) def test_components_preceding_absolute_component_are_ignored(self): self.assertEqual('|c|d', self.filesystem.joinpaths('a', '|b', '|c', 'd')) def test_one_separator_added_for_trailing_empty_components(self): self.assertEqual('a|', self.filesystem.joinpaths('a', '')) self.assertEqual('a|', self.filesystem.joinpaths('a', '', '')) def test_no_separator_added_for_leading_empty_components(self): self.assertEqual('a', self.filesystem.joinpaths('', 'a')) def test_internal_empty_components_ignored(self): self.assertEqual('a|b', self.filesystem.joinpaths('a', '', 'b')) self.assertEqual('a|b|', self.filesystem.joinpaths('a|', '', 'b|')) class PathSeparatorTest(TestCase): def test_os_path_sep_matches_fake_filesystem_separator(self): filesystem = fake_filesystem.FakeFilesystem(path_separator='!') fake_os = fake_filesystem.FakeOsModule(filesystem) self.assertEqual('!', fake_os.sep) self.assertEqual('!', fake_os.path.sep) class NormalizeCaseTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.filesystem.is_case_sensitive = False def test_normalize_case(self): self.filesystem.create_file('/Foo/Bar') self.assertEqual('/Foo/Bar', self.filesystem._original_path('/foo/bar')) self.assertEqual('/Foo/Bar', self.filesystem._original_path('/FOO/BAR')) def test_normalize_case_for_drive(self): self.filesystem.is_windows_fs = True self.filesystem.create_file('C:/Foo/Bar') self.assertEqual('C:/Foo/Bar', self.filesystem._original_path('c:/foo/bar')) self.assertEqual('C:/Foo/Bar', self.filesystem._original_path('C:/FOO/BAR')) def test_normalize_case_for_non_existing_file(self): self.filesystem.create_dir('/Foo/Bar') self.assertEqual('/Foo/Bar/baz', self.filesystem._original_path('/foo/bar/baz')) self.assertEqual('/Foo/Bar/BAZ', self.filesystem._original_path('/FOO/BAR/BAZ')) @unittest.skipIf(not TestCase.is_windows, 'Regression test for Windows problem only') def test_normalize_case_for_lazily_added_empty_file(self): # regression test for specific issue with added empty real files filesystem = fake_filesystem.FakeFilesystem() real_dir_path = os.path.split( os.path.dirname(os.path.abspath(__file__)))[0] filesystem.add_real_directory(real_dir_path) initPyPath = os.path.join(real_dir_path, '__init__.py') self.assertEqual(initPyPath, filesystem._original_path(initPyPath.upper())) class AlternativePathSeparatorTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!') self.filesystem.alternative_path_separator = '?' def test_initial_value(self): filesystem = fake_filesystem.FakeFilesystem() if self.is_windows: self.assertEqual('/', filesystem.alternative_path_separator) else: self.assertIsNone(filesystem.alternative_path_separator) filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.assertIsNone(filesystem.alternative_path_separator) def test_alt_sep(self): fake_os = fake_filesystem.FakeOsModule(self.filesystem) self.assertEqual('?', fake_os.altsep) self.assertEqual('?', fake_os.path.altsep) def test_collapse_path_with_mixed_separators(self): self.assertEqual('!foo!bar', self.filesystem.normpath('!foo??bar')) def test_normalize_path_with_mixed_separators(self): path = 'foo?..?bar' self.assertEqual('!bar', self.filesystem.absnormpath(path)) def test_exists_with_mixed_separators(self): self.filesystem.create_file('?foo?bar?baz') self.filesystem.create_file('!foo!bar!xyzzy!plugh') self.assertTrue(self.filesystem.exists('!foo!bar!baz')) self.assertTrue(self.filesystem.exists('?foo?bar?xyzzy?plugh')) class DriveLetterSupportTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!') self.filesystem.alternative_path_separator = '^' self.filesystem.is_windows_fs = True def test_initial_value(self): filesystem = fake_filesystem.FakeFilesystem() if self.is_windows: self.assertTrue(filesystem.is_windows_fs) else: self.assertFalse(filesystem.is_windows_fs) def test_collapse_path(self): self.assertEqual('c:!foo!bar', self.filesystem.normpath('c:!!foo!!bar')) def test_collapse_unc_path(self): self.assertEqual('!!foo!bar!baz', self.filesystem.normpath('!!foo!bar!!baz!!')) def test_normalize_path_str(self): self.filesystem.cwd = '' self.assertEqual('c:!foo!bar', self.filesystem.absnormpath('c:!foo!!bar')) self.filesystem.cwd = 'c:!foo' self.assertEqual('c:!foo!bar', self.filesystem.absnormpath('bar')) def test_normalize_path_bytes(self): self.filesystem.cwd = b'' self.assertEqual(b'c:!foo!bar', self.filesystem.absnormpath(b'c:!foo!!bar')) self.filesystem.cwd = b'c:!foo' self.assertEqual(b'c:!foo!bar', self.filesystem.absnormpath(b'bar')) def test_split_path_str(self): self.assertEqual(('c:!foo', 'bar'), self.filesystem.splitpath('c:!foo!bar')) self.assertEqual(('c:!', 'foo'), self.filesystem.splitpath('c:!foo')) self.assertEqual(('!foo', 'bar'), self.filesystem.splitpath('!foo!bar')) self.assertEqual(('!', 'foo'), self.filesystem.splitpath('!foo')) self.assertEqual(('c:foo', 'bar'), self.filesystem.splitpath('c:foo!bar')) self.assertEqual(('c:', 'foo'), self.filesystem.splitpath('c:foo')) self.assertEqual(('foo', 'bar'), self.filesystem.splitpath('foo!bar')) def test_split_with_alt_separator(self): self.assertEqual(('a^b', 'c'), self.filesystem.splitpath('a^b^c')) self.assertEqual(('a^b!c', 'd'), self.filesystem.splitpath('a^b!c^d')) self.assertEqual(('a^b!c', 'd'), self.filesystem.splitpath('a^b!c!d')) self.assertEqual((b'a^b', b'c'), self.filesystem.splitpath(b'a^b^c')) self.assertEqual((b'a^b!c', b'd'), self.filesystem.splitpath(b'a^b!c^d')) self.assertEqual((b'a^b!c', b'd'), self.filesystem.splitpath(b'a^b!c!d')) def test_split_path_bytes(self): self.assertEqual((b'c:!foo', b'bar'), self.filesystem.splitpath(b'c:!foo!bar')) self.assertEqual((b'c:!', b'foo'), self.filesystem.splitpath(b'c:!foo')) self.assertEqual((b'!foo', b'bar'), self.filesystem.splitpath(b'!foo!bar')) self.assertEqual((b'!', b'foo'), self.filesystem.splitpath(b'!foo')) self.assertEqual((b'c:foo', b'bar'), self.filesystem.splitpath(b'c:foo!bar')) self.assertEqual((b'c:', b'foo'), self.filesystem.splitpath(b'c:foo')) self.assertEqual((b'foo', b'bar'), self.filesystem.splitpath(b'foo!bar')) def test_characters_before_root_ignored_in_join_paths(self): self.assertEqual('c:d', self.filesystem.joinpaths('b', 'c:', 'd')) def test_resolve_path(self): self.assertEqual('c:!foo!bar', self.filesystem.resolve_path('c:!foo!bar')) def test_get_path_components(self): self.assertEqual(['c:', 'foo', 'bar'], self.filesystem._path_components('c:!foo!bar')) self.assertEqual(['c:'], self.filesystem._path_components('c:')) def test_split_drive_str(self): self.assertEqual(('c:', '!foo!bar'), self.filesystem.splitdrive('c:!foo!bar')) self.assertEqual(('', '!foo!bar'), self.filesystem.splitdrive('!foo!bar')) self.assertEqual(('c:', 'foo!bar'), self.filesystem.splitdrive('c:foo!bar')) self.assertEqual(('', 'foo!bar'), self.filesystem.splitdrive('foo!bar')) def test_split_drive_bytes(self): self.assertEqual((b'c:', b'!foo!bar'), self.filesystem.splitdrive(b'c:!foo!bar')) self.assertEqual((b'', b'!foo!bar'), self.filesystem.splitdrive(b'!foo!bar')) def test_split_drive_alt_sep(self): self.assertEqual(('c:', '^foo^bar'), self.filesystem.splitdrive('c:^foo^bar')) self.assertEqual(('', 'foo^bar'), self.filesystem.splitdrive('foo^bar')) self.assertEqual(('', 'foo^bar!baz'), self.filesystem.splitdrive('foo^bar!baz')) self.assertEqual((b'c:', b'^foo^bar'), self.filesystem.splitdrive(b'c:^foo^bar')) self.assertEqual((b'', b'^foo^bar'), self.filesystem.splitdrive(b'^foo^bar')) self.assertEqual((b'', b'^foo^bar!baz'), self.filesystem.splitdrive(b'^foo^bar!baz')) def test_split_drive_with_unc_path(self): self.assertEqual(('!!foo!bar', '!baz'), self.filesystem.splitdrive('!!foo!bar!baz')) self.assertEqual(('', '!!foo'), self.filesystem.splitdrive('!!foo')) self.assertEqual(('', '!!foo!!bar'), self.filesystem.splitdrive('!!foo!!bar')) self.assertEqual(('!!foo!bar', '!!'), self.filesystem.splitdrive('!!foo!bar!!')) def test_split_drive_with_unc_path_alt_sep(self): self.assertEqual(('^^foo^bar', '!baz'), self.filesystem.splitdrive('^^foo^bar!baz')) self.assertEqual(('', '^^foo'), self.filesystem.splitdrive('^^foo')) self.assertEqual(('', '^^foo^^bar'), self.filesystem.splitdrive('^^foo^^bar')) self.assertEqual(('^^foo^bar', '^^'), self.filesystem.splitdrive('^^foo^bar^^')) def test_split_path_with_drive(self): self.assertEqual(('d:!foo', 'baz'), self.filesystem.splitpath('d:!foo!baz')) self.assertEqual(('d:!foo!baz', ''), self.filesystem.splitpath('d:!foo!baz!')) self.assertEqual(('c:', ''), self.filesystem.splitpath('c:')) self.assertEqual(('c:!', ''), self.filesystem.splitpath('c:!')) self.assertEqual(('c:!!', ''), self.filesystem.splitpath('c:!!')) def test_split_path_with_drive_alt_sep(self): self.assertEqual(('d:^foo', 'baz'), self.filesystem.splitpath('d:^foo^baz')) self.assertEqual(('d:^foo^baz', ''), self.filesystem.splitpath('d:^foo^baz^')) self.assertEqual(('c:', ''), self.filesystem.splitpath('c:')) self.assertEqual(('c:^', ''), self.filesystem.splitpath('c:^')) self.assertEqual(('c:^^', ''), self.filesystem.splitpath('c:^^')) def test_split_path_with_unc_path(self): self.assertEqual(('!!foo!bar!', 'baz'), self.filesystem.splitpath('!!foo!bar!baz')) self.assertEqual(('!!foo!bar', ''), self.filesystem.splitpath('!!foo!bar')) self.assertEqual(('!!foo!bar!!', ''), self.filesystem.splitpath('!!foo!bar!!')) def test_split_path_with_unc_path_alt_sep(self): self.assertEqual(('^^foo^bar^', 'baz'), self.filesystem.splitpath('^^foo^bar^baz')) self.assertEqual(('^^foo^bar', ''), self.filesystem.splitpath('^^foo^bar')) self.assertEqual(('^^foo^bar^^', ''), self.filesystem.splitpath('^^foo^bar^^')) class DiskSpaceTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!', total_size=100) self.os = fake_filesystem.FakeOsModule(self.filesystem) def test_disk_usage_on_file_creation(self): fake_open = fake_filesystem.FakeFileOpen(self.filesystem) total_size = 100 self.filesystem.add_mount_point('mount', total_size) def create_too_large_file(): with fake_open('!mount!file', 'w') as dest: dest.write('a' * (total_size + 1)) with self.assertRaises(OSError): create_too_large_file() self.assertEqual(0, self.filesystem.get_disk_usage('!mount').used) with fake_open('!mount!file', 'w') as dest: dest.write('a' * total_size) self.assertEqual(total_size, self.filesystem.get_disk_usage('!mount').used) def test_file_system_size_after_large_file_creation(self): filesystem = fake_filesystem.FakeFilesystem( path_separator='!', total_size=1024 * 1024 * 1024 * 100) filesystem.create_file('!foo!baz', st_size=1024 * 1024 * 1024 * 10) self.assertEqual((1024 * 1024 * 1024 * 100, 1024 * 1024 * 1024 * 10, 1024 * 1024 * 1024 * 90), filesystem.get_disk_usage()) def test_file_system_size_after_binary_file_creation(self): self.filesystem.create_file('!foo!bar', contents=b'xyzzy') self.assertEqual((100, 5, 95), self.filesystem.get_disk_usage()) def test_file_system_size_after_ascii_string_file_creation(self): self.filesystem.create_file('!foo!bar', contents='complicated') self.assertEqual((100, 11, 89), self.filesystem.get_disk_usage()) def test_filesystem_size_after_2byte_unicode_file_creation(self): self.filesystem.create_file('!foo!bar', contents='Ñложно', encoding='utf-8') self.assertEqual((100, 12, 88), self.filesystem.get_disk_usage()) def test_filesystem_size_after_3byte_unicode_file_creation(self): self.filesystem.create_file('!foo!bar', contents='複雑', encoding='utf-8') self.assertEqual((100, 6, 94), self.filesystem.get_disk_usage()) def test_file_system_size_after_file_deletion(self): self.filesystem.create_file('!foo!bar', contents=b'xyzzy') self.filesystem.create_file('!foo!baz', st_size=20) self.filesystem.remove_object('!foo!bar') self.assertEqual((100, 20, 80), self.filesystem.get_disk_usage()) def test_file_system_size_after_directory_removal(self): self.filesystem.create_file('!foo!bar', st_size=10) self.filesystem.create_file('!foo!baz', st_size=20) self.filesystem.create_file('!foo1!bar', st_size=40) self.filesystem.remove_object('!foo') self.assertEqual((100, 40, 60), self.filesystem.get_disk_usage()) def test_creating_file_with_fitting_content(self): initial_usage = self.filesystem.get_disk_usage() try: self.filesystem.create_file('!foo!bar', contents=b'a' * 100) except OSError: self.fail('File with contents fitting into disk space ' 'could not be written.') self.assertEqual(initial_usage.used + 100, self.filesystem.get_disk_usage().used) def test_creating_file_with_content_too_large(self): def create_large_file(): self.filesystem.create_file('!foo!bar', contents=b'a' * 101) initial_usage = self.filesystem.get_disk_usage() with self.assertRaises(OSError): create_large_file() self.assertEqual(initial_usage, self.filesystem.get_disk_usage()) def test_creating_file_with_fitting_size(self): initial_usage = self.filesystem.get_disk_usage() try: self.filesystem.create_file('!foo!bar', st_size=100) except OSError: self.fail( 'File with size fitting into disk space could not be written.') self.assertEqual(initial_usage.used + 100, self.filesystem.get_disk_usage().used) def test_creating_file_with_size_too_large(self): initial_usage = self.filesystem.get_disk_usage() def create_large_file(): self.filesystem.create_file('!foo!bar', st_size=101) with self.assertRaises(OSError): create_large_file() self.assertEqual(initial_usage, self.filesystem.get_disk_usage()) def test_resize_file_with_fitting_size(self): file_object = self.filesystem.create_file('!foo!bar', st_size=50) try: file_object.set_large_file_size(100) file_object.set_contents(b'a' * 100) except OSError: self.fail( 'Resizing file failed although disk space was sufficient.') def test_resize_file_with_size_too_large(self): file_object = self.filesystem.create_file('!foo!bar', st_size=50) self.assert_raises_os_error(errno.ENOSPC, file_object.set_large_file_size, 200) self.assert_raises_os_error(errno.ENOSPC, file_object.set_contents, 'a' * 150) def test_file_system_size_after_directory_rename(self): self.filesystem.create_file('!foo!bar', st_size=20) self.os.rename('!foo', '!baz') self.assertEqual(20, self.filesystem.get_disk_usage().used) def test_file_system_size_after_file_rename(self): self.filesystem.create_file('!foo!bar', st_size=20) self.os.rename('!foo!bar', '!foo!baz') self.assertEqual(20, self.filesystem.get_disk_usage().used) def test_that_hard_link_does_not_change_used_size(self): file1_path = 'test_file1' file2_path = 'test_file2' self.filesystem.create_file(file1_path, st_size=20) self.assertEqual(20, self.filesystem.get_disk_usage().used) # creating a hard link shall not increase used space self.os.link(file1_path, file2_path) self.assertEqual(20, self.filesystem.get_disk_usage().used) # removing a file shall not decrease used space # if a hard link still exists self.os.unlink(file1_path) self.assertEqual(20, self.filesystem.get_disk_usage().used) self.os.unlink(file2_path) self.assertEqual(0, self.filesystem.get_disk_usage().used) def test_that_the_size_of_correct_mount_point_is_used(self): self.filesystem.add_mount_point('!mount_limited', total_size=50) self.filesystem.add_mount_point('!mount_unlimited') self.assert_raises_os_error(errno.ENOSPC, self.filesystem.create_file, '!mount_limited!foo', st_size=60) self.assert_raises_os_error(errno.ENOSPC, self.filesystem.create_file, '!bar', st_size=110) try: self.filesystem.create_file('!foo', st_size=60) self.filesystem.create_file('!mount_limited!foo', st_size=40) self.filesystem.create_file('!mount_unlimited!foo', st_size=1000000) except OSError: self.fail('File with contents fitting into ' 'disk space could not be written.') def test_that_disk_usage_of_correct_mount_point_is_used(self): self.filesystem.add_mount_point('!mount1', total_size=20) self.filesystem.add_mount_point('!mount1!bar!mount2', total_size=50) self.filesystem.create_file('!foo!bar', st_size=10) self.filesystem.create_file('!mount1!foo!bar', st_size=10) self.filesystem.create_file('!mount1!bar!mount2!foo!bar', st_size=10) self.assertEqual(90, self.filesystem.get_disk_usage('!foo').free) self.assertEqual(10, self.filesystem.get_disk_usage('!mount1!foo').free) self.assertEqual(40, self.filesystem.get_disk_usage( '!mount1!bar!mount2').free) def test_set_larger_disk_size(self): self.filesystem.add_mount_point('!mount1', total_size=20) self.assert_raises_os_error(errno.ENOSPC, self.filesystem.create_file, '!mount1!foo', st_size=100) self.filesystem.set_disk_usage(total_size=200, path='!mount1') self.filesystem.create_file('!mount1!foo', st_size=100) self.assertEqual(100, self.filesystem.get_disk_usage('!mount1!foo').free) def test_set_smaller_disk_size(self): self.filesystem.add_mount_point('!mount1', total_size=200) self.filesystem.create_file('!mount1!foo', st_size=100) self.assert_raises_os_error(errno.ENOSPC, self.filesystem.set_disk_usage, total_size=50, path='!mount1') self.filesystem.set_disk_usage(total_size=150, path='!mount1') self.assertEqual(50, self.filesystem.get_disk_usage('!mount1!foo').free) def test_disk_size_on_unlimited_disk(self): self.filesystem.add_mount_point('!mount1') self.filesystem.create_file('!mount1!foo', st_size=100) self.filesystem.set_disk_usage(total_size=1000, path='!mount1') self.assertEqual(900, self.filesystem.get_disk_usage('!mount1!foo').free) def test_disk_size_on_auto_mounted_drive_on_file_creation(self): self.filesystem.is_windows_fs = True # drive d: shall be auto-mounted and the used size adapted self.filesystem.create_file('d:!foo!bar', st_size=100) self.filesystem.set_disk_usage(total_size=1000, path='d:') self.assertEqual(self.filesystem.get_disk_usage('d:!foo').free, 900) def test_disk_size_on_auto_mounted_drive_on_directory_creation(self): self.filesystem.is_windows_fs = True self.filesystem.create_dir('d:!foo!bar') self.filesystem.create_file('d:!foo!bar!baz', st_size=100) self.filesystem.create_file('d:!foo!baz', st_size=100) self.filesystem.set_disk_usage(total_size=1000, path='d:') self.assertEqual(self.filesystem.get_disk_usage('d:!foo').free, 800) def test_copying_preserves_byte_contents(self): source_file = self.filesystem.create_file('foo', contents=b'somebytes') dest_file = self.filesystem.create_file('bar') dest_file.set_contents(source_file.contents) self.assertEqual(dest_file.contents, source_file.contents) class MountPointTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='!', total_size=100) self.filesystem.add_mount_point('!foo') self.filesystem.add_mount_point('!bar') self.filesystem.add_mount_point('!foo!baz') def test_that_new_mount_points_get_new_device_number(self): self.assertEqual(1, self.filesystem.get_object('!').st_dev) self.assertEqual(2, self.filesystem.get_object('!foo').st_dev) self.assertEqual(3, self.filesystem.get_object('!bar').st_dev) self.assertEqual(4, self.filesystem.get_object('!foo!baz').st_dev) def test_that_new_directories_get_correct_device_number(self): self.assertEqual(1, self.filesystem.create_dir('!foo1!bar').st_dev) self.assertEqual(2, self.filesystem.create_dir('!foo!bar').st_dev) self.assertEqual(4, self.filesystem.create_dir('!foo!baz!foo!bar').st_dev) def test_that_new_files_get_correct_device_number(self): self.assertEqual(1, self.filesystem.create_file('!foo1!bar').st_dev) self.assertEqual(2, self.filesystem.create_file('!foo!bar').st_dev) self.assertEqual(4, self.filesystem.create_file( '!foo!baz!foo!bar').st_dev) def test_that_mount_point_cannot_be_added_twice(self): self.assert_raises_os_error(errno.EEXIST, self.filesystem.add_mount_point, '!foo') self.assert_raises_os_error(errno.EEXIST, self.filesystem.add_mount_point, '!foo!') def test_that_drives_are_auto_mounted(self): self.filesystem.is_windows_fs = True self.filesystem.create_dir('d:!foo!bar') self.filesystem.create_file('d:!foo!baz') self.filesystem.create_file('z:!foo!baz') self.assertEqual(5, self.filesystem.get_object('d:').st_dev) self.assertEqual(5, self.filesystem.get_object('d:!foo!bar').st_dev) self.assertEqual(5, self.filesystem.get_object('d:!foo!baz').st_dev) self.assertEqual(6, self.filesystem.get_object('z:!foo!baz').st_dev) def test_that_drives_are_auto_mounted_case_insensitive(self): self.filesystem.is_windows_fs = True self.filesystem.is_case_sensitive = False self.filesystem.create_dir('D:!foo!bar') self.filesystem.create_file('e:!foo!baz') self.assertEqual(5, self.filesystem.get_object('D:').st_dev) self.assertEqual(5, self.filesystem.get_object('d:!foo!bar').st_dev) self.assertEqual(6, self.filesystem.get_object('e:!foo').st_dev) self.assertEqual(6, self.filesystem.get_object('E:!Foo!Baz').st_dev) def test_that_unc_paths_are_auto_mounted(self): self.filesystem.is_windows_fs = True self.filesystem.create_dir('!!foo!bar!baz') self.filesystem.create_file('!!foo!bar!bip!bop') self.assertEqual(5, self.filesystem.get_object('!!foo!bar').st_dev) self.assertEqual(5, self.filesystem.get_object( '!!foo!bar!bip!bop').st_dev) class ConvenienceMethodTest(RealFsTestCase): def test_create_link_with_non_existent_parent(self): self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') link_path = self.make_path('nonexistent', 'test_file2') self.filesystem.create_file(file1_path, contents='link test') self.assertEqual(self.os.stat(file1_path).st_nlink, 1) self.filesystem.create_link(file1_path, link_path) self.assertEqual(self.os.stat(file1_path).st_nlink, 2) self.assertTrue(self.filesystem.exists(link_path)) def test_create_symlink_with_non_existent_parent(self): self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') link_path = self.make_path('nonexistent', 'test_file2') self.filesystem.create_file(file1_path, contents='symlink test') self.filesystem.create_symlink(link_path, file1_path) self.assertTrue(self.filesystem.exists(link_path)) self.assertTrue(self.filesystem.islink(link_path)) class RealFileSystemAccessTest(RealFsTestCase): def setUp(self): # use the real path separator to work with the real file system self.filesystem = fake_filesystem.FakeFilesystem() self.fake_open = fake_filesystem.FakeFileOpen(self.filesystem) self.pyfakefs_path = os.path.split( os.path.dirname(os.path.abspath(__file__)))[0] self.root_path = os.path.split(self.pyfakefs_path)[0] def test_add_non_existing_real_file_raises(self): nonexisting_path = os.path.join('nonexisting', 'test.txt') with self.assertRaises(OSError): self.filesystem.add_real_file(nonexisting_path) self.assertFalse(self.filesystem.exists(nonexisting_path)) def test_add_non_existing_real_directory_raises(self): nonexisting_path = '/nonexisting' self.assert_raises_os_error(errno.ENOENT, self.filesystem.add_real_directory, nonexisting_path) self.assertFalse(self.filesystem.exists(nonexisting_path)) def test_existing_fake_file_raises(self): real_file_path = __file__ self.filesystem.create_file(real_file_path) self.assert_raises_os_error(errno.EEXIST, self.filesystem.add_real_file, real_file_path) def test_existing_fake_directory_raises(self): self.filesystem.create_dir(self.root_path) self.assert_raises_os_error(errno.EEXIST, self.filesystem.add_real_directory, self.root_path) def check_fake_file_stat(self, fake_file, real_file_path, target_path=None): if target_path is None or target_path == real_file_path: self.assertTrue(self.filesystem.exists(real_file_path)) else: self.assertFalse(self.filesystem.exists(real_file_path)) self.assertTrue(self.filesystem.exists(target_path)) real_stat = os.stat(real_file_path) self.assertIsNone(fake_file._byte_contents) self.assertEqual(fake_file.st_size, real_stat.st_size) self.assertAlmostEqual(fake_file.st_ctime, real_stat.st_ctime, places=5) self.assertAlmostEqual(fake_file.st_atime, real_stat.st_atime, places=5) self.assertAlmostEqual(fake_file.st_mtime, real_stat.st_mtime, places=5) self.assertEqual(fake_file.st_uid, real_stat.st_uid) self.assertEqual(fake_file.st_gid, real_stat.st_gid) def check_read_only_file(self, fake_file, real_file_path): with open(real_file_path, 'rb') as f: real_contents = f.read() self.assertEqual(fake_file.byte_contents, real_contents) if not is_root(): self.assert_raises_os_error( errno.EACCES, self.fake_open, real_file_path, 'w') else: with self.fake_open(real_file_path, 'w'): pass def check_writable_file(self, fake_file, real_file_path): with open(real_file_path, 'rb') as f: real_contents = f.read() self.assertEqual(fake_file.byte_contents, real_contents) with self.fake_open(real_file_path, 'wb') as f: f.write(b'test') with open(real_file_path, 'rb') as f: real_contents1 = f.read() self.assertEqual(real_contents1, real_contents) with self.fake_open(real_file_path, 'rb') as f: fake_contents = f.read() self.assertEqual(fake_contents, b'test') def test_add_existing_real_file_read_only(self): real_file_path = os.path.abspath(__file__) fake_file = self.filesystem.add_real_file(real_file_path) self.check_fake_file_stat(fake_file, real_file_path) self.assertEqual(fake_file.st_mode & 0o333, 0) self.check_read_only_file(fake_file, real_file_path) def test_add_existing_real_file_read_write(self): real_file_path = os.path.realpath(__file__) fake_file = self.filesystem.add_real_file(real_file_path, read_only=False) self.check_fake_file_stat(fake_file, real_file_path) self.assertEqual(fake_file.st_mode, os.stat(real_file_path).st_mode) self.check_writable_file(fake_file, real_file_path) def test_add_real_file_to_existing_path(self): real_file_path = os.path.abspath(__file__) self.filesystem.create_file('/foo/bar') self.assert_raises_os_error( errno.EEXIST, self.filesystem.add_real_file, real_file_path, target_path='/foo/bar') def test_add_real_file_to_non_existing_path(self): real_file_path = os.path.abspath(__file__) fake_file = self.filesystem.add_real_file(real_file_path, target_path='/foo/bar') self.check_fake_file_stat(fake_file, real_file_path, target_path='/foo/bar') def test_write_to_real_file(self): # regression test for #470 real_file_path = os.path.abspath(__file__) self.filesystem.add_real_file(real_file_path, read_only=False) with self.fake_open(real_file_path, 'w') as f: f.write('foo') with self.fake_open(real_file_path, 'rb') as f: self.assertEqual(b'foo', f.read()) def test_add_existing_real_directory_read_only(self): self.filesystem.add_real_directory(self.pyfakefs_path) self.assertTrue(self.filesystem.exists(self.pyfakefs_path)) self.assertTrue(self.filesystem.exists( os.path.join(self.pyfakefs_path, 'fake_filesystem.py'))) self.assertTrue(self.filesystem.exists( os.path.join(self.pyfakefs_path, 'fake_pathlib.py'))) file_path = os.path.join(self.pyfakefs_path, 'fake_filesystem_shutil.py') fake_file = self.filesystem.resolve(file_path) self.check_fake_file_stat(fake_file, file_path) self.check_read_only_file(fake_file, file_path) def test_add_existing_real_directory_tree(self): self.filesystem.add_real_directory(self.root_path) self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fake_filesystem_test.py'))) self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'fake_filesystem.py'))) self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', '__init__.py'))) @contextlib.contextmanager def create_symlinks(self, symlinks): for link in symlinks: os.symlink(link[0], link[1]) yield for link in symlinks: os.unlink(link[1]) def test_add_existing_real_directory_symlink(self): fake_open = fake_filesystem.FakeFileOpen(self.filesystem) real_directory = os.path.join(self.root_path, 'pyfakefs', 'tests') symlinks = [ ('..', os.path.join( real_directory, 'fixtures', 'symlink_dir_relative')), ('../all_tests.py', os.path.join( real_directory, 'fixtures', 'symlink_file_relative')), (real_directory, os.path.join( real_directory, 'fixtures', 'symlink_dir_absolute')), (os.path.join(real_directory, 'all_tests.py'), os.path.join( real_directory, 'fixtures', 'symlink_file_absolute')), ('/etc/something', os.path.join( real_directory, 'fixtures', 'symlink_file_absolute_outside')), ] self.filesystem.create_file('/etc/something') with fake_open('/etc/something', 'w') as f: f.write('good morning') try: with self.create_symlinks(symlinks): self.filesystem.add_real_directory( real_directory, lazy_read=False) except OSError: if self.is_windows: raise unittest.SkipTest( 'Symlinks under Windows need admin privileges') raise for link in symlinks: self.assertTrue(self.filesystem.islink(link[1])) # relative self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_dir_relative'))) self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_dir_relative/all_tests.py'))) self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_file_relative'))) # absolute self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_dir_absolute'))) self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_dir_absolute/all_tests.py'))) self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_file_absolute'))) # outside self.assertTrue( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_file_absolute_outside'))) self.assertEqual( fake_open(os.path.join( self.root_path, 'pyfakefs', 'tests', 'fixtures/symlink_file_absolute_outside')).read(), 'good morning' ) def test_add_existing_real_directory_symlink_target_path(self): self.skip_if_symlink_not_supported(force_real_fs=True) real_directory = os.path.join(self.root_path, 'pyfakefs', 'tests') symlinks = [ ('..', os.path.join( real_directory, 'fixtures', 'symlink_dir_relative')), ('../all_tests.py', os.path.join( real_directory, 'fixtures', 'symlink_file_relative')), ] with self.create_symlinks(symlinks): self.filesystem.add_real_directory( real_directory, target_path='/path', lazy_read=False) self.assertTrue(self.filesystem.exists( '/path/fixtures/symlink_dir_relative')) self.assertTrue(self.filesystem.exists( '/path/fixtures/symlink_dir_relative/all_tests.py')) self.assertTrue(self.filesystem.exists( '/path/fixtures/symlink_file_relative')) def test_add_existing_real_directory_symlink_lazy_read(self): self.skip_if_symlink_not_supported(force_real_fs=True) real_directory = os.path.join(self.root_path, 'pyfakefs', 'tests') symlinks = [ ('..', os.path.join( real_directory, 'fixtures', 'symlink_dir_relative')), ('../all_tests.py', os.path.join( real_directory, 'fixtures', 'symlink_file_relative')), ] with self.create_symlinks(symlinks): self.filesystem.add_real_directory( real_directory, target_path='/path', lazy_read=True) self.assertTrue(self.filesystem.exists( '/path/fixtures/symlink_dir_relative')) self.assertTrue(self.filesystem.exists( '/path/fixtures/symlink_dir_relative/all_tests.py')) self.assertTrue(self.filesystem.exists( '/path/fixtures/symlink_file_relative')) def test_add_existing_real_directory_tree_to_existing_path(self): self.filesystem.create_dir('/foo/bar') self.assert_raises_os_error(errno.EEXIST, self.filesystem.add_real_directory, self.root_path, target_path='/foo/bar') def test_add_existing_real_directory_tree_to_other_path(self): self.filesystem.add_real_directory(self.root_path, target_path='/foo/bar') self.assertFalse( self.filesystem.exists( os.path.join(self.pyfakefs_path, 'tests', 'fake_filesystem_test.py'))) self.assertTrue( self.filesystem.exists( os.path.join('foo', 'bar', 'pyfakefs', 'tests', 'fake_filesystem_test.py'))) self.assertFalse( self.filesystem.exists( os.path.join(self.root_path, 'pyfakefs', 'fake_filesystem.py'))) self.assertTrue( self.filesystem.exists( os.path.join('foo', 'bar', 'pyfakefs', '__init__.py'))) def test_get_object_from_lazily_added_real_directory(self): self.filesystem.is_case_sensitive = True self.filesystem.add_real_directory(self.root_path) self.assertTrue(self.filesystem.get_object( os.path.join(self.root_path, 'pyfakefs', 'fake_filesystem.py'))) self.assertTrue( self.filesystem.get_object( os.path.join(self.root_path, 'pyfakefs', '__init__.py'))) def test_add_existing_real_directory_lazily(self): disk_size = 1024 * 1024 * 1024 real_dir_path = os.path.join(self.root_path, 'pyfakefs') self.filesystem.set_disk_usage(disk_size, real_dir_path) self.filesystem.add_real_directory(real_dir_path) # the directory contents have not been read, the the disk usage # has not changed self.assertEqual(disk_size, self.filesystem.get_disk_usage(real_dir_path).free) # checking for existence shall read the directory contents self.assertTrue( self.filesystem.get_object( os.path.join(real_dir_path, 'fake_filesystem.py'))) # so now the free disk space shall have decreased self.assertGreater(disk_size, self.filesystem.get_disk_usage(real_dir_path).free) def test_add_existing_real_directory_not_lazily(self): disk_size = 1024 * 1024 * 1024 self.filesystem.set_disk_usage(disk_size, self.pyfakefs_path) self.filesystem.add_real_directory(self.pyfakefs_path, lazy_read=False) # the directory has been read, so the file sizes have # been subtracted from the free space self.assertGreater(disk_size, self.filesystem.get_disk_usage( self.pyfakefs_path).free) def test_add_existing_real_directory_read_write(self): self.filesystem.add_real_directory(self.pyfakefs_path, read_only=False) self.assertTrue(self.filesystem.exists(self.pyfakefs_path)) self.assertTrue(self.filesystem.exists( os.path.join(self.pyfakefs_path, 'fake_filesystem.py'))) self.assertTrue(self.filesystem.exists( os.path.join(self.pyfakefs_path, 'fake_pathlib.py'))) file_path = os.path.join(self.pyfakefs_path, 'pytest_plugin.py') fake_file = self.filesystem.resolve(file_path) self.check_fake_file_stat(fake_file, file_path) self.check_writable_file(fake_file, file_path) def test_add_existing_real_paths_read_only(self): real_file_path = os.path.realpath(__file__) fixture_path = os.path.join(self.pyfakefs_path, 'tests', 'fixtures') self.filesystem.add_real_paths([real_file_path, fixture_path]) fake_file = self.filesystem.resolve(real_file_path) self.check_fake_file_stat(fake_file, real_file_path) self.check_read_only_file(fake_file, real_file_path) real_file_path = os.path.join(fixture_path, 'module_with_attributes.py') fake_file = self.filesystem.resolve(real_file_path) self.check_fake_file_stat(fake_file, real_file_path) self.check_read_only_file(fake_file, real_file_path) def test_add_existing_real_paths_read_write(self): real_file_path = os.path.realpath(__file__) fixture_path = os.path.join(self.pyfakefs_path, 'tests', 'fixtures') self.filesystem.add_real_paths([real_file_path, fixture_path], read_only=False) fake_file = self.filesystem.resolve(real_file_path) self.check_fake_file_stat(fake_file, real_file_path) self.check_writable_file(fake_file, real_file_path) real_file_path = os.path.join(fixture_path, 'module_with_attributes.py') fake_file = self.filesystem.resolve(real_file_path) self.check_fake_file_stat(fake_file, real_file_path) self.check_writable_file(fake_file, real_file_path) class FileSideEffectTests(TestCase): def side_effect(self): test_case = self test_case.side_effect_called = False def __side_effect(file_object): test_case.side_effect_called = True test_case.side_effect_file_object_content = file_object.contents return __side_effect def setUp(self): # use the real path separator to work with the real file system self.filesystem = fake_filesystem.FakeFilesystem() self.filesystem.create_file('/a/b/file_one', side_effect=self.side_effect()) def test_side_effect_called(self): fake_open = fake_filesystem.FakeFileOpen(self.filesystem) self.side_effect_called = False with fake_open('/a/b/file_one', 'w') as handle: handle.write('foo') self.assertTrue(self.side_effect_called) def test_side_effect_file_object(self): fake_open = fake_filesystem.FakeFileOpen(self.filesystem) self.side_effect_called = False with fake_open('/a/b/file_one', 'w') as handle: handle.write('foo') self.assertEqual(self.side_effect_file_object_content, 'foo') if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_filesystem_unittest_test.py0000666000000000000000000010006514147521400023162 0ustar 00000000000000# Copyright 2014 Altera Corporation. All Rights Reserved. # Copyright 2015-2017 John McGehee # Author: John McGehee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Test the :py:class`pyfakefs.fake_filesystem_unittest.TestCase` base class. """ import glob import io import multiprocessing import os import pathlib import runpy import shutil import sys import tempfile import unittest import warnings from distutils.dir_util import copy_tree, remove_tree from pathlib import Path from unittest import TestCase, mock import pyfakefs.tests.import_as_example import pyfakefs.tests.logsio from pyfakefs import fake_filesystem_unittest, fake_filesystem from pyfakefs.fake_filesystem import OSType from pyfakefs.fake_filesystem_unittest import ( Patcher, Pause, patchfs, PatchMode ) from pyfakefs.tests.fixtures import module_with_attributes class TestPatcher(TestCase): def test_context_manager(self): with Patcher() as patcher: patcher.fs.create_file('/foo/bar', contents='test') with open('/foo/bar') as f: contents = f.read() self.assertEqual('test', contents) @patchfs def test_context_decorator(self, fake_fs): fake_fs.create_file('/foo/bar', contents='test') with open('/foo/bar') as f: contents = f.read() self.assertEqual('test', contents) class TestPatchfsArgumentOrder(TestCase): @patchfs @mock.patch('os.system') def test_argument_order1(self, fake_fs, patched_system): fake_fs.create_file('/foo/bar', contents='test') with open('/foo/bar') as f: contents = f.read() self.assertEqual('test', contents) os.system("foo") patched_system.assert_called_with("foo") @mock.patch('os.system') @patchfs def test_argument_order2(self, patched_system, fake_fs): fake_fs.create_file('/foo/bar', contents='test') with open('/foo/bar') as f: contents = f.read() self.assertEqual('test', contents) os.system("foo") patched_system.assert_called_with("foo") class TestPyfakefsUnittestBase(fake_filesystem_unittest.TestCase): def setUp(self): """Set up the fake file system""" self.setUpPyfakefs() class TestPyfakefsUnittest(TestPyfakefsUnittestBase): # pylint: disable=R0904 """Test the `pyfakefs.fake_filesystem_unittest.TestCase` base class.""" def test_open(self): """Fake `open()` function is bound""" self.assertFalse(os.path.exists('/fake_file.txt')) with open('/fake_file.txt', 'w') as f: f.write("This test file was created using the open() function.\n") self.assertTrue(self.fs.exists('/fake_file.txt')) with open('/fake_file.txt') as f: content = f.read() self.assertEqual('This test file was created using the ' 'open() function.\n', content) def test_io_open(self): """Fake io module is bound""" self.assertFalse(os.path.exists('/fake_file.txt')) with io.open('/fake_file.txt', 'w') as f: f.write("This test file was created using the" " io.open() function.\n") self.assertTrue(self.fs.exists('/fake_file.txt')) with open('/fake_file.txt') as f: content = f.read() self.assertEqual('This test file was created using the ' 'io.open() function.\n', content) def test_os(self): """Fake os module is bound""" self.assertFalse(self.fs.exists('/test/dir1/dir2')) os.makedirs('/test/dir1/dir2') self.assertTrue(self.fs.exists('/test/dir1/dir2')) def test_glob(self): """Fake glob module is bound""" is_windows = sys.platform.startswith('win') self.assertEqual([], glob.glob('/test/dir1/dir*')) self.fs.create_dir('/test/dir1/dir2a') matching_paths = glob.glob('/test/dir1/dir*') if is_windows: self.assertEqual([r'/test/dir1\dir2a'], matching_paths) else: self.assertEqual(['/test/dir1/dir2a'], matching_paths) self.fs.create_dir('/test/dir1/dir2b') matching_paths = sorted(glob.glob('/test/dir1/dir*')) if is_windows: self.assertEqual([r'/test/dir1\dir2a', r'/test/dir1\dir2b'], matching_paths) else: self.assertEqual(['/test/dir1/dir2a', '/test/dir1/dir2b'], matching_paths) def test_shutil(self): """Fake shutil module is bound""" self.fs.create_dir('/test/dir1/dir2a') self.fs.create_dir('/test/dir1/dir2b') self.assertTrue(self.fs.exists('/test/dir1/dir2b')) self.assertTrue(self.fs.exists('/test/dir1/dir2a')) shutil.rmtree('/test/dir1') self.assertFalse(self.fs.exists('/test/dir1')) def test_fakepathlib(self): with pathlib.Path('/fake_file.txt') as p: with p.open('w') as f: f.write('text') is_windows = sys.platform.startswith('win') if is_windows: self.assertTrue(self.fs.exists(r'\fake_file.txt')) else: self.assertTrue(self.fs.exists('/fake_file.txt')) class TestPatchingImports(TestPyfakefsUnittestBase): def test_import_as_other_name(self): file_path = '/foo/bar/baz' self.fs.create_file(file_path) self.assertTrue(self.fs.exists(file_path)) self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists1(file_path)) def test_import_path_from_os(self): """Make sure `from os import path` patches `path`.""" file_path = '/foo/bar/baz' self.fs.create_file(file_path) self.assertTrue(self.fs.exists(file_path)) self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists2(file_path)) def test_import_path_from_pathlib(self): file_path = '/foo/bar' self.fs.create_dir(file_path) self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists3(file_path)) def test_import_function_from_os_path(self): file_path = '/foo/bar' self.fs.create_dir(file_path) self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists5(file_path)) def test_import_function_from_os_path_as_other_name(self): file_path = '/foo/bar' self.fs.create_dir(file_path) self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists6(file_path)) def test_import_pathlib_path(self): file_path = '/foo/bar' self.fs.create_dir(file_path) self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists7(file_path)) def test_import_function_from_os(self): file_path = '/foo/bar' self.fs.create_file(file_path, contents=b'abc') stat_result = pyfakefs.tests.import_as_example.file_stat1(file_path) self.assertEqual(3, stat_result.st_size) def test_import_function_from_os_as_other_name(self): file_path = '/foo/bar' self.fs.create_file(file_path, contents=b'abc') stat_result = pyfakefs.tests.import_as_example.file_stat2(file_path) self.assertEqual(3, stat_result.st_size) def test_import_open_as_other_name(self): file_path = '/foo/bar' self.fs.create_file(file_path, contents=b'abc') contents = pyfakefs.tests.import_as_example.file_contents1(file_path) self.assertEqual('abc', contents) def test_import_io_open_as_other_name(self): file_path = '/foo/bar' self.fs.create_file(file_path, contents=b'abc') contents = pyfakefs.tests.import_as_example.file_contents2(file_path) self.assertEqual('abc', contents) class TestPatchingDefaultArgs(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs(patch_default_args=True) def test_path_exists_as_default_arg_in_function(self): file_path = '/foo/bar' self.fs.create_dir(file_path) self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists4(file_path)) def test_path_exists_as_default_arg_in_method(self): file_path = '/foo/bar' self.fs.create_dir(file_path) sut = pyfakefs.tests.import_as_example.TestDefaultArg() self.assertTrue(sut.check_if_exists(file_path)) def test_fake_path_exists4(self): self.fs.create_file('foo') self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists4('foo')) class TestAttributesWithFakeModuleNames(TestPyfakefsUnittestBase): """Test that module attributes with names like `path` or `io` are not stubbed out. """ def test_attributes(self): """Attributes of module under test are not patched""" self.assertEqual(module_with_attributes.os, 'os attribute value') self.assertEqual(module_with_attributes.path, 'path attribute value') self.assertEqual(module_with_attributes.pathlib, 'pathlib attribute value') self.assertEqual(module_with_attributes.shutil, 'shutil attribute value') self.assertEqual(module_with_attributes.io, 'io attribute value') import math as path # noqa: E402 wanted import not at top class TestPathNotPatchedIfNotOsPath(TestPyfakefsUnittestBase): """Tests that `path` is not patched if it is not `os.path`. An own path module (in this case an alias to math) can be imported and used. """ def test_own_path_module(self): self.assertEqual(2, path.floor(2.5)) class FailedPatchingTest(TestPyfakefsUnittestBase): """Negative tests: make sure the tests for `modules_to_reload` and `modules_to_patch` fail if not providing the arguments. """ @unittest.expectedFailure def test_system_stat(self): file_path = '/foo/bar' self.fs.create_file(file_path, contents=b'test') self.assertEqual( 4, pyfakefs.tests.import_as_example.system_stat(file_path).st_size) class ReloadModuleTest(fake_filesystem_unittest.TestCase): """Make sure that reloading a module allows patching of classes not patched automatically. """ def setUp(self): """Set up the fake file system""" self.setUpPyfakefs( modules_to_reload=[pyfakefs.tests.import_as_example]) class NoSkipNamesTest(fake_filesystem_unittest.TestCase): """Reference test for additional_skip_names tests: make sure that the module is patched by default.""" def setUp(self): self.setUpPyfakefs() def test_path_exists(self): self.assertFalse( pyfakefs.tests.import_as_example.exists_this_file()) def test_fake_path_exists1(self): self.fs.create_file('foo') self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists1('foo')) def test_fake_path_exists2(self): self.fs.create_file('foo') self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists2('foo')) def test_fake_path_exists3(self): self.fs.create_file('foo') self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists3('foo')) def test_fake_path_exists5(self): self.fs.create_file('foo') self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists5('foo')) def test_fake_path_exists6(self): self.fs.create_file('foo') self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists6('foo')) def test_fake_path_exists7(self): self.fs.create_file('foo') self.assertTrue( pyfakefs.tests.import_as_example.check_if_exists7('foo')) def test_open_fails(self): with self.assertRaises(OSError): pyfakefs.tests.import_as_example.open_this_file() def test_open_patched_in_module_ending_with_io(self): # regression test for #569 file_path = '/foo/bar' self.fs.create_file(file_path, contents=b'abc') contents = pyfakefs.tests.logsio.file_contents(file_path) self.assertEqual(b'abc', contents) class AdditionalSkipNamesTest(fake_filesystem_unittest.TestCase): """Make sure that modules in additional_skip_names are not patched. Passes module name to `additional_skip_names`.""" def setUp(self): self.setUpPyfakefs( additional_skip_names=['pyfakefs.tests.import_as_example']) def test_path_exists(self): self.assertTrue( pyfakefs.tests.import_as_example.exists_this_file()) def test_fake_path_does_not_exist1(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists1('foo')) def test_fake_path_does_not_exist2(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists2('foo')) def test_fake_path_does_not_exist3(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists3('foo')) def test_fake_path_does_not_exist4(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists4('foo')) def test_fake_path_does_not_exist5(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists5('foo')) def test_fake_path_does_not_exist6(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists6('foo')) def test_fake_path_does_not_exist7(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists7('foo')) def test_open_succeeds(self): pyfakefs.tests.import_as_example.open_this_file() def test_path_succeeds(self): pyfakefs.tests.import_as_example.return_this_file_path() class AdditionalSkipNamesModuleTest(fake_filesystem_unittest.TestCase): """Make sure that modules in additional_skip_names are not patched. Passes module to `additional_skip_names`.""" def setUp(self): self.setUpPyfakefs( additional_skip_names=[pyfakefs.tests.import_as_example]) def test_path_exists(self): self.assertTrue( pyfakefs.tests.import_as_example.exists_this_file()) def test_fake_path_does_not_exist1(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists1('foo')) def test_fake_path_does_not_exist2(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists2('foo')) def test_fake_path_does_not_exist3(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists3('foo')) def test_fake_path_does_not_exist4(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists4('foo')) def test_fake_path_does_not_exist5(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists5('foo')) def test_fake_path_does_not_exist6(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists6('foo')) def test_fake_path_does_not_exist7(self): self.fs.create_file('foo') self.assertFalse( pyfakefs.tests.import_as_example.check_if_exists7('foo')) def test_open_succeeds(self): pyfakefs.tests.import_as_example.open_this_file() def test_path_succeeds(self): pyfakefs.tests.import_as_example.return_this_file_path() class FakeExampleModule: """Used to patch a function that uses system-specific functions that cannot be patched automatically.""" _orig_module = pyfakefs.tests.import_as_example def __init__(self, fs): pass def system_stat(self, filepath): return os.stat(filepath) def __getattr__(self, name): """Forwards any non-faked calls to the standard module.""" return getattr(self._orig_module, name) class PatchModuleTest(fake_filesystem_unittest.TestCase): """Make sure that reloading a module allows patching of classes not patched automatically. """ def setUp(self): """Set up the fake file system""" self.setUpPyfakefs( modules_to_patch={ 'pyfakefs.tests.import_as_example': FakeExampleModule}) def test_system_stat(self): file_path = '/foo/bar' self.fs.create_file(file_path, contents=b'test') self.assertEqual( 4, pyfakefs.tests.import_as_example.system_stat(file_path).st_size) class PatchModuleTestUsingDecorator(unittest.TestCase): """Make sure that reloading a module allows patching of classes not patched automatically - use patchfs decorator with parameter. """ @patchfs @unittest.expectedFailure def test_system_stat_failing(self, fake_fs): file_path = '/foo/bar' fake_fs.create_file(file_path, contents=b'test') self.assertEqual( 4, pyfakefs.tests.import_as_example.system_stat(file_path).st_size) @patchfs(modules_to_patch={ 'pyfakefs.tests.import_as_example': FakeExampleModule}) def test_system_stat(self, fake_fs): file_path = '/foo/bar' fake_fs.create_file(file_path, contents=b'test') self.assertEqual( 4, pyfakefs.tests.import_as_example.system_stat(file_path).st_size) class NoRootUserTest(fake_filesystem_unittest.TestCase): """Test allow_root_user argument to setUpPyfakefs.""" def setUp(self): self.setUpPyfakefs(allow_root_user=False) def test_non_root_behavior(self): """Check that fs behaves as non-root user regardless of actual user rights. """ self.fs.is_windows_fs = False dir_path = '/foo/bar' self.fs.create_dir(dir_path, perm_bits=0o555) file_path = dir_path + 'baz' with self.assertRaises(OSError): self.fs.create_file(file_path) file_path = '/baz' self.fs.create_file(file_path) os.chmod(file_path, 0o400) with self.assertRaises(OSError): open(file_path, 'w') class PauseResumeTest(fake_filesystem_unittest.TestCase): def setUp(self): self.real_temp_file = None self.setUpPyfakefs() def tearDown(self): if self.real_temp_file is not None: self.real_temp_file.close() def test_pause_resume(self): fake_temp_file = tempfile.NamedTemporaryFile() self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) self.pause() self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertFalse(os.path.exists(fake_temp_file.name)) self.real_temp_file = tempfile.NamedTemporaryFile() self.assertFalse(self.fs.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(self.real_temp_file.name)) self.resume() self.assertFalse(os.path.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) def test_pause_resume_fs(self): fake_temp_file = tempfile.NamedTemporaryFile() self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) # resume does nothing if not paused self.fs.resume() self.assertTrue(os.path.exists(fake_temp_file.name)) self.fs.pause() self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertFalse(os.path.exists(fake_temp_file.name)) self.real_temp_file = tempfile.NamedTemporaryFile() self.assertFalse(self.fs.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(self.real_temp_file.name)) # pause does nothing if already paused self.fs.pause() self.assertFalse(self.fs.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(self.real_temp_file.name)) self.fs.resume() self.assertFalse(os.path.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) def test_pause_resume_contextmanager(self): fake_temp_file = tempfile.NamedTemporaryFile() self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) with Pause(self): self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertFalse(os.path.exists(fake_temp_file.name)) self.real_temp_file = tempfile.NamedTemporaryFile() self.assertFalse(self.fs.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(self.real_temp_file.name)) self.assertFalse(os.path.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) def test_pause_resume_fs_contextmanager(self): fake_temp_file = tempfile.NamedTemporaryFile() self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) with Pause(self.fs): self.assertTrue(self.fs.exists(fake_temp_file.name)) self.assertFalse(os.path.exists(fake_temp_file.name)) self.real_temp_file = tempfile.NamedTemporaryFile() self.assertFalse(self.fs.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(self.real_temp_file.name)) self.assertFalse(os.path.exists(self.real_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) def test_pause_resume_without_patcher(self): fs = fake_filesystem.FakeFilesystem() with self.assertRaises(RuntimeError): fs.resume() class PauseResumePatcherTest(fake_filesystem_unittest.TestCase): def test_pause_resume(self): with Patcher() as p: fake_temp_file = tempfile.NamedTemporaryFile() self.assertTrue(p.fs.exists(fake_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) p.pause() self.assertTrue(p.fs.exists(fake_temp_file.name)) self.assertFalse(os.path.exists(fake_temp_file.name)) real_temp_file = tempfile.NamedTemporaryFile() self.assertFalse(p.fs.exists(real_temp_file.name)) self.assertTrue(os.path.exists(real_temp_file.name)) p.resume() self.assertFalse(os.path.exists(real_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) real_temp_file.close() def test_pause_resume_contextmanager(self): with Patcher() as p: fake_temp_file = tempfile.NamedTemporaryFile() self.assertTrue(p.fs.exists(fake_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) with Pause(p): self.assertTrue(p.fs.exists(fake_temp_file.name)) self.assertFalse(os.path.exists(fake_temp_file.name)) real_temp_file = tempfile.NamedTemporaryFile() self.assertFalse(p.fs.exists(real_temp_file.name)) self.assertTrue(os.path.exists(real_temp_file.name)) self.assertFalse(os.path.exists(real_temp_file.name)) self.assertTrue(os.path.exists(fake_temp_file.name)) real_temp_file.close() class TestPyfakefsTestCase(unittest.TestCase): def setUp(self): class TestTestCase(fake_filesystem_unittest.TestCase): def runTest(self): pass self.test_case = TestTestCase('runTest') def test_test_case_type(self): self.assertIsInstance(self.test_case, unittest.TestCase) self.assertIsInstance(self.test_case, fake_filesystem_unittest.TestCaseMixin) class TestTempFileReload(unittest.TestCase): """Regression test for #356 to make sure that reloading the tempfile does not affect other tests.""" def test_fakefs(self): with Patcher() as patcher: patcher.fs.create_file('/mytempfile', contents='abcd') def test_value(self): v = multiprocessing.Value('I', 0) self.assertEqual(v.value, 0) class TestPyfakefsTestCaseMixin(unittest.TestCase, fake_filesystem_unittest.TestCaseMixin): def test_set_up_pyfakefs(self): self.setUpPyfakefs() self.assertTrue(hasattr(self, 'fs')) self.assertIsInstance(self.fs, fake_filesystem.FakeFilesystem) class TestShutilWithZipfile(fake_filesystem_unittest.TestCase): """Regression test for #427.""" def setUp(self): self.setUpPyfakefs() self.fs.create_file('foo/bar') def test_a(self): shutil.make_archive('archive', 'zip', root_dir='foo') def test_b(self): # used to fail because 'bar' could not be found shutil.make_archive('archive', 'zip', root_dir='foo') class TestDistutilsCopyTree(fake_filesystem_unittest.TestCase): """Regression test for #501.""" def setUp(self): self.setUpPyfakefs() self.fs.create_dir("./test/subdir/") self.fs.create_dir("./test/subdir2/") self.fs.create_file("./test2/subdir/1.txt") def test_file_copied(self): copy_tree("./test2/", "./test/") remove_tree("./test2/") self.assertTrue(os.path.isfile('./test/subdir/1.txt')) self.assertFalse(os.path.isdir('./test2/')) def test_file_copied_again(self): # used to fail because 'test2' could not be found self.assertTrue(os.path.isfile('./test2/subdir/1.txt')) copy_tree("./test2/", "./test/") remove_tree("./test2/") self.assertTrue(os.path.isfile('./test/subdir/1.txt')) self.assertFalse(os.path.isdir('./test2/')) class PathlibTest(TestCase): """Regression test for #527""" @patchfs def test_cwd(self, fs): """Make sure fake file system is used for os in pathlib""" self.assertEqual(os.path.sep, str(pathlib.Path.cwd())) dot_abs = pathlib.Path(".").absolute() self.assertEqual(os.path.sep, str(dot_abs)) self.assertTrue(dot_abs.exists()) class TestDeprecationSuppression(fake_filesystem_unittest.TestCase): @unittest.skipIf(sys.version_info[1] == 6, 'Test fails for Python 3.6 for unknown reason') def test_no_deprecation_warning(self): """Ensures that deprecation warnings are suppressed during module lookup, see #542. """ from pyfakefs.tests.fixtures.deprecated_property import \ DeprecationTest # noqa: F401 with warnings.catch_warnings(record=True) as w: warnings.simplefilter("error", DeprecationWarning) self.setUpPyfakefs() self.assertEqual(0, len(w)) def load_configs(configs): """ Helper code for patching open_code in auto mode, see issue #554. """ retval = [] for config in configs: if len(config) > 3 and config[-3:] == ".py": retval += runpy.run_path(config) else: retval += runpy.run_module(config) return retval class AutoPatchOpenCodeTestCase(fake_filesystem_unittest.TestCase): """ Test patching open_code in auto mode, see issue #554.""" def setUp(self): self.setUpPyfakefs(patch_open_code=PatchMode.AUTO) self.configpy = 'configpy.py' self.fs.create_file( self.configpy, contents="configurable_value='yup'") self.config_module = 'pyfakefs.tests.fixtures.config_module' def test_both(self): load_configs([self.configpy, self.config_module]) def test_run_path(self): load_configs([self.configpy]) def test_run_module(self): load_configs([self.config_module]) class TestOtherFS(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs() def test_real_file_with_home(self): """Regression test for #558""" self.fs.is_windows_fs = os.name != 'nt' self.fs.add_real_file(__file__) with open(__file__) as f: self.assertTrue(f.read()) home = Path.home() if sys.version_info < (3, 6): # fspath support since Python 3.6 home = str(home) os.chdir(home) with open(__file__) as f: self.assertTrue(f.read()) def test_windows(self): self.fs.os = OSType.WINDOWS path = r'C:\foo\bar' self.assertEqual(path, os.path.join('C:\\', 'foo', 'bar')) self.assertEqual(('C:', r'\foo\bar'), os.path.splitdrive(path)) self.fs.create_file(path) self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path.upper())) self.assertTrue(os.path.ismount(r'\\share\foo')) self.assertTrue(os.path.ismount(r'C:')) self.assertEqual('\\', os.sep) self.assertEqual('\\', os.path.sep) self.assertEqual('/', os.altsep) self.assertEqual(';', os.pathsep) self.assertEqual('\r\n', os.linesep) self.assertEqual('nul', os.devnull) def test_linux(self): self.fs.os = OSType.LINUX path = '/foo/bar' self.assertEqual(path, os.path.join('/', 'foo', 'bar')) self.assertEqual(('', 'C:/foo/bar'), os.path.splitdrive('C:/foo/bar')) self.fs.create_file(path) self.assertTrue(os.path.exists(path)) self.assertFalse(os.path.exists(path.upper())) self.assertTrue(os.path.ismount('/')) self.assertFalse(os.path.ismount('//share/foo')) self.assertEqual('/', os.sep) self.assertEqual('/', os.path.sep) self.assertEqual(None, os.altsep) self.assertEqual(':', os.pathsep) self.assertEqual('\n', os.linesep) self.assertEqual('/dev/null', os.devnull) def test_macos(self): self.fs.os = OSType.MACOS path = '/foo/bar' self.assertEqual(path, os.path.join('/', 'foo', 'bar')) self.assertEqual(('', 'C:/foo/bar'), os.path.splitdrive('C:/foo/bar')) self.fs.create_file(path) self.assertTrue(os.path.exists(path)) self.assertTrue(os.path.exists(path.upper())) self.assertTrue(os.path.ismount('/')) self.assertFalse(os.path.ismount('//share/foo')) self.assertEqual('/', os.sep) self.assertEqual('/', os.path.sep) self.assertEqual(None, os.altsep) self.assertEqual(':', os.pathsep) self.assertEqual('\n', os.linesep) self.assertEqual('/dev/null', os.devnull) def test_drivelike_path(self): self.fs.os = OSType.LINUX folder = Path('/test') file_path = folder / 'C:/testfile' file_path.parent.mkdir(parents=True) file_path.touch() # use str() to be Python 3.5 compatible os.chdir(str(folder)) self.assertTrue(os.path.exists(str(file_path.relative_to(folder)))) if __name__ == "__main__": unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_filesystem_vs_real_test.py0000666000000000000000000006647314166355054022767 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Test that FakeFilesystem calls work identically to a real filesystem.""" # pylint: disable-all import os import shutil import sys import tempfile import time import unittest from pyfakefs import fake_filesystem from pyfakefs.helpers import IS_PYPY def sep(path): """Converts slashes in the path to the architecture's path seperator.""" if isinstance(path, str): return path.replace('/', os.sep) return path def _get_errno(raised_error): if raised_error is not None: try: return raised_error.errno except AttributeError: pass class TestCase(unittest.TestCase): is_windows = sys.platform.startswith('win') _FAKE_FS_BASE = sep('/fakefs') class FakeFilesystemVsRealTest(TestCase): def _paths(self, path): """For a given path, return paths in the real and fake filesystems.""" if not path: return None, None return (os.path.join(self.real_base, path), os.path.join(self.fake_base, path)) def _create_test_file(self, file_type, path, contents=None): """Create a dir, file, or link in both the real fs and the fake.""" path = sep(path) self._created_files.append([file_type, path, contents]) real_path, fake_path = self._paths(path) if file_type == 'd': os.mkdir(real_path) self.fake_os.mkdir(fake_path) if file_type == 'f': fh = open(real_path, 'w') fh.write(contents or '') fh.close() fh = self.fake_open(fake_path, 'w') fh.write(contents or '') fh.close() # b for binary file if file_type == 'b': fh = open(real_path, 'wb') fh.write(contents or '') fh.close() fh = self.fake_open(fake_path, 'wb') fh.write(contents or '') fh.close() # l for symlink, h for hard link if file_type in ('l', 'h'): real_target, fake_target = (contents, contents) # If it begins with '/', make it relative to the base. You can't go # creating files in / for the real file system. if contents.startswith(os.sep): real_target, fake_target = self._paths(contents[1:]) if file_type == 'l': os.symlink(real_target, real_path) self.fake_os.symlink(fake_target, fake_path) elif file_type == 'h': os.link(real_target, real_path) self.fake_os.link(fake_target, fake_path) def setUp(self): # Base paths in the real and test file systems. We keep them different # so that missing features in the fake don't fall through to the base # operations and magically succeed. tsname = 'fakefs.%s' % time.time() self.cwd = os.getcwd() # Fully expand the base_path - required on OS X. self.real_base = os.path.realpath( os.path.join(tempfile.gettempdir(), tsname)) os.chdir(tempfile.gettempdir()) if os.path.isdir(self.real_base): shutil.rmtree(self.real_base) os.mkdir(self.real_base) self.fake_base = self._FAKE_FS_BASE # Make sure we can write to the physical testing temp directory. self.assertTrue(os.access(self.real_base, os.W_OK)) self.fake_filesystem = fake_filesystem.FakeFilesystem() self.fake_filesystem.create_dir(self.fake_base) self.fake_os = fake_filesystem.FakeOsModule(self.fake_filesystem) self.fake_open = fake_filesystem.FakeFileOpen(self.fake_filesystem) self._created_files = [] os.chdir(self.real_base) self.fake_os.chdir(self.fake_base) def tearDown(self): # We have to remove all the files from the real FS. Doing the same for # the fake FS is optional, but doing it is an extra sanity check. os.chdir(tempfile.gettempdir()) try: rev_files = self._created_files[:] rev_files.reverse() for info in rev_files: real_path, fake_path = self._paths(info[1]) if info[0] == 'd': try: os.rmdir(real_path) except OSError as e: if 'Directory not empty' in e: self.fail('Real path %s not empty: %s : %s' % ( real_path, e, os.listdir(real_path))) else: raise self.fake_os.rmdir(fake_path) if info[0] == 'f' or info[0] == 'l': os.remove(real_path) self.fake_os.remove(fake_path) finally: shutil.rmtree(self.real_base) os.chdir(self.cwd) def _compare_behaviors(self, method_name, path, real, fake, method_returns_path=False): """Invoke an os method in both real and fake contexts and compare results. Invoke a real filesystem method with a path to a real file and invoke a fake filesystem method with a path to a fake file and compare the results. We expect some calls to throw Exceptions, so we catch those and compare them. Args: method_name: Name of method being tested, for use in error messages. path: potential path to a file in the real and fake file systems, passing an empty tuple indicates that no arguments to pass to method. real: built-in system library or method from the built-in system library which takes a path as an arg and returns some value. fake: fake_filsystem object or method from a fake_filesystem class which takes a path as an arg and returns some value. method_returns_path: True if the method returns a path, and thus we must compensate for expected difference between real and fake. Returns: A description of the difference in behavior, or None. """ # pylint: disable=C6403 def _error_class(exc): if exc: if hasattr(exc, 'errno'): return '{}({})'.format(exc.__class__.__name__, exc.errno) return exc.__class__.__name__ return 'None' real_err, real_value = self._get_real_value(method_name, path, real) fake_err, fake_value = self._get_fake_value(method_name, path, fake) method_call = f'{method_name}' method_call += '()' if path == () else '({path})' # We only compare on the error class because the acutal error contents # is almost always different because of the file paths. if _error_class(real_err) != _error_class(fake_err): if real_err is None: return '%s: real version returned %s, fake raised %s' % ( method_call, real_value, _error_class(fake_err)) if fake_err is None: return '%s: real version raised %s, fake returned %s' % ( method_call, _error_class(real_err), fake_value) return '%s: real version raised %s, fake raised %s' % ( method_call, _error_class(real_err), _error_class(fake_err)) real_errno = _get_errno(real_err) fake_errno = _get_errno(fake_err) if real_errno != fake_errno: return '%s(%s): both raised %s, real errno %s, fake errno %s' % ( method_name, path, _error_class(real_err), real_errno, fake_errno) # If the method is supposed to return a full path AND both values # begin with the expected full path, then trim it off. if method_returns_path: if (real_value and fake_value and real_value.startswith(self.real_base) and fake_value.startswith(self.fake_base)): real_value = real_value[len(self.real_base):] fake_value = fake_value[len(self.fake_base):] if real_value != fake_value: return '%s: real return %s, fake returned %s' % ( method_call, real_value, fake_value) return None @staticmethod def _get_fake_value(method_name, path, fake): fake_value = None fake_err = None try: fake_method = fake if not callable(fake): fake_method = getattr(fake, method_name) args = [] if path == () else [path] result = fake_method(*args) if isinstance(result, bytes): fake_value = result.decode() else: fake_value = str(result) except Exception as e: # pylint: disable-msg=W0703 fake_err = e return fake_err, fake_value @staticmethod def _get_real_value(method_name, path, real): real_value = None real_err = None # Catching Exception below gives a lint warning, but it's what we need. try: args = [] if path == () else [path] real_method = real if not callable(real): real_method = getattr(real, method_name) result = real_method(*args) if isinstance(result, bytes): real_value = result.decode() else: real_value = str(result) except Exception as e: # pylint: disable-msg=W0703 real_err = e return real_err, real_value def assertOsMethodBehaviorMatches(self, method_name, path, method_returns_path=False): """Invoke an os method in both real and fake contexts and compare. For a given method name (from the os module) and a path, compare the behavior of the system provided module against the fake_filesystem module. We expect results and/or Exceptions raised to be identical. Args: method_name: Name of method being tested. path: potential path to a file in the real and fake file systems. method_returns_path: True if the method returns a path, and thus we must compensate for expected difference between real and fake. Returns: A description of the difference in behavior, or None. """ path = sep(path) return self._compare_behaviors(method_name, path, os, self.fake_os, method_returns_path) def diff_open_method_behavior(self, method_name, path, mode, data, method_returns_data=True): """Invoke an open method in both real and fkae contexts and compare. Args: method_name: Name of method being tested. path: potential path to a file in the real and fake file systems. mode: how to open the file. data: any data to pass to the method. method_returns_data: True if a method returns some sort of data. For a given method name (from builtin open) and a path, compare the behavior of the system provided module against the fake_filesystem module. We expect results and/or Exceptions raised to be identical. Returns: A description of the difference in behavior, or None. """ with open(path, mode) as real_fh: with self.fake_open(path, mode) as fake_fh: return self._compare_behaviors( method_name, data, real_fh, fake_fh, method_returns_data) def diff_os_path_method_behavior(self, method_name, path, method_returns_path=False): """Invoke an os.path method in both real and fake contexts and compare. For a given method name (from the os.path module) and a path, compare the behavior of the system provided module against the fake_filesytem module. We expect results and/or Exceptions raised to be identical. Args: method_name: Name of method being tested. path: potential path to a file in the real and fake file systems. method_returns_path: True if the method returns a path, and thus we must compensate for expected difference between real and fake. Returns: A description of the difference in behavior, or None. """ return self._compare_behaviors(method_name, path, os.path, self.fake_os.path, method_returns_path) def assertOsPathMethodBehaviorMatches(self, method_name, path, method_returns_path=False): """Assert that an os.path behaves the same in both real and fake contexts. Wraps DiffOsPathMethodBehavior, raising AssertionError if any differences are reported. Args: method_name: Name of method being tested. path: potential path to a file in the real and fake file systems. method_returns_path: True if the method returns a path, and thus we must compensate for expected difference between real and fake. Raises: AssertionError if there is any difference in behavior. """ path = sep(path) diff = self.diff_os_path_method_behavior( method_name, path, method_returns_path) if diff: self.fail(diff) def assertAllOsBehaviorsMatch(self, path): path = sep(path) os_method_names = [] if self.is_windows else ['readlink'] os_method_names_no_args = ['getcwd'] os_path_method_names = ['isabs', 'isdir'] if not self.is_windows: os_path_method_names += ['islink', 'lexists'] if not self.is_windows or not IS_PYPY: os_path_method_names += ['isfile', 'exists'] wrapped_methods = [ ['access', self._access_real, self._access_fake], ['stat.size', self._stat_size_real, self._stat_size_fake], ['lstat.size', self._lstat_size_real, self._lstat_size_fake] ] differences = [] for method_name in os_method_names: diff = self.assertOsMethodBehaviorMatches(method_name, path) if diff: differences.append(diff) for method_name in os_method_names_no_args: diff = self.assertOsMethodBehaviorMatches(method_name, (), method_returns_path=True) if diff: differences.append(diff) for method_name in os_path_method_names: diff = self.diff_os_path_method_behavior(method_name, path) if diff: differences.append(diff) for m in wrapped_methods: diff = self._compare_behaviors(m[0], path, m[1], m[2]) if diff: differences.append(diff) if differences: self.fail('Behaviors do not match for %s:\n %s' % (path, '\n '.join(differences))) def assertFileHandleBehaviorsMatch(self, path, mode, data): path = sep(path) write_method_names = ['write', 'writelines'] read_method_names = ['read', 'readlines'] other_method_names = ['truncate', 'flush', 'close'] differences = [] for method_name in write_method_names: diff = self.diff_open_method_behavior( method_name, path, mode, data) if diff: differences.append(diff) for method_name in read_method_names + other_method_names: diff = self.diff_open_method_behavior(method_name, path, mode, ()) if diff: differences.append(diff) if differences: self.fail('Behaviors do not match for %s:\n %s' % (path, '\n '.join(differences))) # Helpers for checks which are not straight method calls. @staticmethod def _access_real(path): return os.access(path, os.R_OK) def _access_fake(self, path): return self.fake_os.access(path, os.R_OK) def _stat_size_real(self, path): real_path, unused_fake_path = self._paths(path) # fake_filesystem.py does not implement stat().st_size for directories if os.path.isdir(real_path): return None return os.stat(real_path).st_size def _stat_size_fake(self, path): unused_real_path, fake_path = self._paths(path) # fake_filesystem.py does not implement stat().st_size for directories if self.fake_os.path.isdir(fake_path): return None return self.fake_os.stat(fake_path).st_size def _lstat_size_real(self, path): real_path, unused_fake_path = self._paths(path) if os.path.isdir(real_path): return None size = os.lstat(real_path).st_size # Account for the difference in the lengths of the absolute paths. if os.path.islink(real_path): if os.readlink(real_path).startswith(os.sep): size -= len(self.real_base) return size def _lstat_size_fake(self, path): unused_real_path, fake_path = self._paths(path) # size = 0 if self.fake_os.path.isdir(fake_path): return None size = self.fake_os.lstat(fake_path).st_size # Account for the difference in the lengths of the absolute paths. if self.fake_os.path.islink(fake_path): if self.fake_os.readlink(fake_path).startswith(os.sep): size -= len(self.fake_base) return size def test_isabs(self): # We do not have to create any files for isabs. self.assertOsPathMethodBehaviorMatches('isabs', None) self.assertOsPathMethodBehaviorMatches('isabs', '') self.assertOsPathMethodBehaviorMatches('isabs', '/') self.assertOsPathMethodBehaviorMatches('isabs', '/a') self.assertOsPathMethodBehaviorMatches('isabs', 'a') def test_none_path(self): self.assertAllOsBehaviorsMatch(None) def test_empty_path(self): self.assertAllOsBehaviorsMatch('') def test_root_path(self): self.assertAllOsBehaviorsMatch('/') def test_non_existant_file(self): self.assertAllOsBehaviorsMatch('foo') def test_empty_file(self): self._create_test_file('f', 'aFile') self.assertAllOsBehaviorsMatch('aFile') def test_file_with_contents(self): self._create_test_file('f', 'aFile', 'some contents') self.assertAllOsBehaviorsMatch('aFile') def test_file_with_binary_contents(self): self._create_test_file('b', 'aFile', b'some contents') self.assertAllOsBehaviorsMatch('aFile') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_sym_link_to_empty_file(self): self._create_test_file('f', 'aFile') self._create_test_file('l', 'link_to_empty', 'aFile') self.assertAllOsBehaviorsMatch('link_to_empty') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_hard_link_to_empty_file(self): self._create_test_file('f', 'aFile') self._create_test_file('h', 'link_to_empty', 'aFile') self.assertAllOsBehaviorsMatch('link_to_empty') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_sym_link_to_real_file(self): self._create_test_file('f', 'aFile', 'some contents') self._create_test_file('l', 'link_to_file', 'aFile') self.assertAllOsBehaviorsMatch('link_to_file') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_hard_link_to_real_file(self): self._create_test_file('f', 'aFile', 'some contents') self._create_test_file('h', 'link_to_file', 'aFile') self.assertAllOsBehaviorsMatch('link_to_file') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_broken_sym_link(self): self._create_test_file('l', 'broken_link', 'broken') self._create_test_file('l', 'loop', '/a/loop') self.assertAllOsBehaviorsMatch('broken_link') def test_file_in_a_folder(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('f', 'a/b/file', 'contents') self.assertAllOsBehaviorsMatch('a/b/file') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_absolute_sym_link_to_folder(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('f', 'a/b/file', 'contents') self._create_test_file('l', 'a/link', '/a/b') self.assertAllOsBehaviorsMatch('a/link/file') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_link_to_folder_after_chdir(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('f', 'a/b/file', 'contents') self._create_test_file('l', 'a/link', '/a/b') real_dir, fake_dir = self._paths('a/b') os.chdir(real_dir) self.fake_os.chdir(fake_dir) self.assertAllOsBehaviorsMatch('file') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_relative_sym_link_to_folder(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('f', 'a/b/file', 'contents') self._create_test_file('l', 'a/link', 'b') self.assertAllOsBehaviorsMatch('a/link/file') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_sym_link_to_parent(self): # Soft links on HFS+ / OS X behave differently. if os.uname()[0] != 'Darwin': self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('l', 'a/b/c', '..') self.assertAllOsBehaviorsMatch('a/b/c') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_path_through_sym_link_to_parent(self): self._create_test_file('d', 'a') self._create_test_file('f', 'a/target', 'contents') self._create_test_file('d', 'a/b') self._create_test_file('l', 'a/b/c', '..') self.assertAllOsBehaviorsMatch('a/b/c/target') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_sym_link_to_sibling_directory(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('d', 'a/sibling_of_b') self._create_test_file('f', 'a/sibling_of_b/target', 'contents') self._create_test_file('l', 'a/b/c', '../sibling_of_b') self.assertAllOsBehaviorsMatch('a/b/c/target') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_sym_link_to_sibling_directory_non_existant_file(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('d', 'a/sibling_of_b') self._create_test_file('f', 'a/sibling_of_b/target', 'contents') self._create_test_file('l', 'a/b/c', '../sibling_of_b') self.assertAllOsBehaviorsMatch('a/b/c/file_does_not_exist') @unittest.skipIf(TestCase.is_windows, 'no symlink in Windows') def test_broken_sym_link_to_sibling_directory(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('d', 'a/sibling_of_b') self._create_test_file('f', 'a/sibling_of_b/target', 'contents') self._create_test_file('l', 'a/b/c', '../broken_sibling_of_b') self.assertAllOsBehaviorsMatch('a/b/c/target') def test_relative_path(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('d', 'a/sibling_of_b') self._create_test_file('f', 'a/sibling_of_b/target', 'contents') self.assertAllOsBehaviorsMatch('a/b/../sibling_of_b/target') def test_broken_relative_path(self): self._create_test_file('d', 'a') self._create_test_file('d', 'a/b') self._create_test_file('d', 'a/sibling_of_b') self._create_test_file('f', 'a/sibling_of_b/target', 'contents') self.assertAllOsBehaviorsMatch('a/b/../broken/target') def test_bad_relative_path(self): self._create_test_file('d', 'a') self._create_test_file('f', 'a/target', 'contents') self._create_test_file('d', 'a/b') self._create_test_file('d', 'a/sibling_of_b') self._create_test_file('f', 'a/sibling_of_b/target', 'contents') self.assertAllOsBehaviorsMatch('a/b/../broken/../target') def test_getmtime_nonexistant_path(self): self.assertOsPathMethodBehaviorMatches('getmtime', 'no/such/path') def test_builtin_open_modes(self): self._create_test_file('f', 'read', 'some contents') self._create_test_file('f', 'write', 'some contents') self._create_test_file('f', 'append', 'some contents') self.assertFileHandleBehaviorsMatch('read', 'r', 'other contents') self.assertFileHandleBehaviorsMatch('write', 'w', 'other contents') self.assertFileHandleBehaviorsMatch('append', 'a', 'other contents') self._create_test_file('f', 'readplus', 'some contents') self._create_test_file('f', 'writeplus', 'some contents') self.assertFileHandleBehaviorsMatch( 'readplus', 'r+', 'other contents') self.assertFileHandleBehaviorsMatch( 'writeplus', 'w+', 'other contents') self._create_test_file('b', 'binaryread', b'some contents') self._create_test_file('b', 'binarywrite', b'some contents') self._create_test_file('b', 'binaryappend', b'some contents') self.assertFileHandleBehaviorsMatch( 'binaryread', 'rb', b'other contents') self.assertFileHandleBehaviorsMatch( 'binarywrite', 'wb', b'other contents') self.assertFileHandleBehaviorsMatch( 'binaryappend', 'ab', b'other contents') self.assertFileHandleBehaviorsMatch('read', 'rb', 'other contents') self.assertFileHandleBehaviorsMatch('write', 'wb', 'other contents') self.assertFileHandleBehaviorsMatch('append', 'ab', 'other contents') def main(_): unittest.main() if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_open_test.py0000666000000000000000000024263714147521400020014 0ustar 00000000000000# -*- coding: utf-8 -*- # # Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for fake_filesystem.FakeOsModule.""" import errno import io import locale import os import stat import sys import time import unittest from pyfakefs import fake_filesystem from pyfakefs.fake_filesystem import is_root, PERM_READ, FakeIoModule from pyfakefs.fake_filesystem_unittest import PatchMode from pyfakefs.tests.test_utils import RealFsTestCase class FakeFileOpenTestBase(RealFsTestCase): def setUp(self): super(FakeFileOpenTestBase, self).setUp() if self.use_real_fs(): self.open = io.open else: self.fake_io_module = FakeIoModule(self.filesystem) self.open = self.fake_io_module.open def path_separator(self): return '!' class FakeFileOpenTest(FakeFileOpenTestBase): def setUp(self): super(FakeFileOpenTest, self).setUp() self.orig_time = time.time def tearDown(self): super(FakeFileOpenTest, self).tearDown() time.time = self.orig_time def test_open_no_parent_dir(self): """Expect raise when opening a file in a missing directory.""" file_path = self.make_path('foo', 'bar.txt') self.assert_raises_os_error(errno.ENOENT, self.open, file_path, 'w') def test_delete_on_close(self): self.skip_real_fs() file_dir = 'boo' file_path = 'boo!far' self.os.mkdir(file_dir) self.open = fake_filesystem.FakeFileOpen(self.filesystem, delete_on_close=True) with self.open(file_path, 'w'): self.assertTrue(self.filesystem.exists(file_path)) self.assertFalse(self.filesystem.exists(file_path)) def test_no_delete_on_close_by_default(self): file_path = self.make_path('czar') with self.open(file_path, 'w'): self.assertTrue(self.os.path.exists(file_path)) self.assertTrue(self.os.path.exists(file_path)) def test_compatibility_of_with_statement(self): self.skip_real_fs() self.open = fake_filesystem.FakeFileOpen(self.filesystem, delete_on_close=True) file_path = 'foo' self.assertFalse(self.os.path.exists(file_path)) with self.open(file_path, 'w'): self.assertTrue(self.os.path.exists(file_path)) # After the 'with' statement, the close() method should have been # called. self.assertFalse(self.os.path.exists(file_path)) def test_unicode_contents(self): file_path = self.make_path('foo') # note that this will work only if the string can be represented # by the locale preferred encoding - which under Windows is # usually not UTF-8, but something like Latin1, depending on the locale text_fractions = 'Ümläüts' try: with self.open(file_path, 'w') as f: f.write(text_fractions) except UnicodeEncodeError: # see https://github.com/jmcgeheeiv/pyfakefs/issues/623 self.skipTest("This test does not work with an ASCII locale") with self.open(file_path) as f: contents = f.read() self.assertEqual(contents, text_fractions) def test_byte_contents(self): file_path = self.make_path('foo') byte_fractions = b'\xe2\x85\x93 \xe2\x85\x94 \xe2\x85\x95 \xe2\x85\x96' with self.open(file_path, 'wb') as f: f.write(byte_fractions) # the encoding has to be specified, otherwise the locale default # is used which can be different on different systems with self.open(file_path, encoding='utf-8') as f: contents = f.read() self.assertEqual(contents, byte_fractions.decode('utf-8')) def test_write_str_read_bytes(self): file_path = self.make_path('foo') str_contents = 'Äsgül' try: with self.open(file_path, 'w') as f: f.write(str_contents) except UnicodeEncodeError: # see https://github.com/jmcgeheeiv/pyfakefs/issues/623 self.skipTest("This test does not work with an ASCII locale") with self.open(file_path, 'rb') as f: contents = f.read() self.assertEqual(str_contents, contents.decode( locale.getpreferredencoding(False))) def test_open_valid_file(self): contents = [ 'I am he as\n', 'you are he as\n', 'you are me and\n', 'we are all together\n' ] file_path = self.make_path('bar.txt') self.create_file(file_path, contents=''.join(contents)) with self.open(file_path) as fake_file: self.assertEqual(contents, fake_file.readlines()) def test_open_valid_args(self): contents = [ "Bang bang Maxwell's silver hammer\n", 'Came down on her head', ] file_path = self.make_path('abbey_road', 'maxwell') self.create_file(file_path, contents=''.join(contents)) with self.open(file_path, buffering=1) as f: self.assertEqual(contents, f.readlines()) with self.open(file_path, buffering=1, errors='strict', newline='\n', opener=None) as f: expected_contents = [contents[0][:-1] + self.os.linesep, contents[1]] self.assertEqual(expected_contents, f.readlines()) def test_open_valid_file_with_cwd(self): contents = [ 'I am he as\n', 'you are he as\n', 'you are me and\n', 'we are all together\n' ] file_path = self.make_path('bar.txt') self.create_file(file_path, contents=''.join(contents)) self.os.chdir(self.base_path) with self.open(file_path) as f: self.assertEqual(contents, f.readlines()) def test_iterate_over_file(self): contents = [ "Bang bang Maxwell's silver hammer", 'Came down on her head', ] file_path = self.make_path('abbey_road', 'maxwell') self.create_file(file_path, contents='\n'.join(contents)) with self.open(file_path) as fake_file: result = [line.rstrip() for line in fake_file] self.assertEqual(contents, result) def test_next_over_file(self): contents = [ 'Live long\n', 'and prosper\n' ] result = [] file_path = self.make_path('foo.txt') self.create_file(file_path, contents=''.join(contents)) with self.open(file_path) as fake_file: result.append(next(fake_file)) result.append(next(fake_file)) self.assertEqual(contents, result) def test_open_directory_error(self): directory_path = self.make_path('foo') self.os.mkdir(directory_path) if self.is_windows: self.assert_raises_os_error(errno.EACCES, self.open.__call__, directory_path) else: self.assert_raises_os_error(errno.EISDIR, self.open.__call__, directory_path) def test_create_file_with_write(self): contents = [ "Here comes the sun, little darlin'", 'Here comes the sun, and I say,', "It's alright", ] file_dir = self.make_path('abbey_road') file_path = self.os.path.join(file_dir, 'here_comes_the_sun') self.os.mkdir(file_dir) with self.open(file_path, 'w') as fake_file: for line in contents: fake_file.write(line + '\n') with self.open(file_path) as fake_file: result = [line.rstrip() for line in fake_file] self.assertEqual(contents, result) def test_create_file_with_append(self): contents = [ "Here comes the sun, little darlin'", 'Here comes the sun, and I say,', "It's alright", ] file_dir = self.make_path('abbey_road') file_path = self.os.path.join(file_dir, 'here_comes_the_sun') self.os.mkdir(file_dir) with self.open(file_path, 'a') as fake_file: for line in contents: fake_file.write(line + '\n') with self.open(file_path) as fake_file: result = [line.rstrip() for line in fake_file] self.assertEqual(contents, result) def test_exclusive_create_file_failure(self): self.skip_if_symlink_not_supported() file_path = self.make_path('bar') self.create_file(file_path) self.assert_raises_os_error(errno.EEXIST, self.open, file_path, 'x') self.assert_raises_os_error(errno.EEXIST, self.open, file_path, 'xb') def test_exclusive_create_file(self): file_dir = self.make_path('foo') file_path = self.os.path.join(file_dir, 'bar') self.os.mkdir(file_dir) contents = 'String contents' with self.open(file_path, 'x') as fake_file: fake_file.write(contents) with self.open(file_path) as fake_file: self.assertEqual(contents, fake_file.read()) def test_exclusive_create_binary_file(self): file_dir = self.make_path('foo') file_path = self.os.path.join(file_dir, 'bar') self.os.mkdir(file_dir) contents = b'Binary contents' with self.open(file_path, 'xb') as fake_file: fake_file.write(contents) with self.open(file_path, 'rb') as fake_file: self.assertEqual(contents, fake_file.read()) def test_overwrite_existing_file(self): file_path = self.make_path('overwite') self.create_file(file_path, contents='To disappear') new_contents = [ 'Only these lines', 'should be in the file.', ] with self.open(file_path, 'w') as fake_file: for line in new_contents: fake_file.write(line + '\n') with self.open(file_path) as fake_file: result = [line.rstrip() for line in fake_file] self.assertEqual(new_contents, result) def test_append_existing_file(self): file_path = self.make_path('appendfile') contents = [ 'Contents of original file' 'Appended contents', ] self.create_file(file_path, contents=contents[0]) with self.open(file_path, 'a') as fake_file: for line in contents[1:]: fake_file.write(line + '\n') with self.open(file_path) as fake_file: result = [line.rstrip() for line in fake_file] self.assertEqual(contents, result) def test_open_with_wplus(self): # set up file_path = self.make_path('wplus_file') self.create_file(file_path, contents='old contents') self.assertTrue(self.os.path.exists(file_path)) with self.open(file_path, 'r') as fake_file: self.assertEqual('old contents', fake_file.read()) # actual tests with self.open(file_path, 'w+') as fake_file: fake_file.write('new contents') fake_file.seek(0) self.assertTrue('new contents', fake_file.read()) def test_open_with_wplus_truncation(self): # set up file_path = self.make_path('wplus_file') self.create_file(file_path, contents='old contents') self.assertTrue(self.os.path.exists(file_path)) with self.open(file_path, 'r') as fake_file: self.assertEqual('old contents', fake_file.read()) # actual tests with self.open(file_path, 'w+') as fake_file: fake_file.seek(0) self.assertEqual('', fake_file.read()) def test_open_with_append_flag(self): contents = [ 'I am he as\n', 'you are he as\n', 'you are me and\n', 'we are all together\n' ] additional_contents = [ 'These new lines\n', 'like you a lot.\n' ] file_path = self.make_path('appendfile') self.create_file(file_path, contents=''.join(contents)) with self.open(file_path, 'a') as fake_file: with self.assertRaises(io.UnsupportedOperation): fake_file.read(0) with self.assertRaises(io.UnsupportedOperation): fake_file.readline() expected_len = len(''.join(contents)) expected_len += len(contents) * (len(self.os.linesep) - 1) self.assertEqual(expected_len, fake_file.tell()) fake_file.seek(0) self.assertEqual(0, fake_file.tell()) fake_file.writelines(additional_contents) with self.open(file_path) as fake_file: self.assertEqual( contents + additional_contents, fake_file.readlines()) def check_append_with_aplus(self): file_path = self.make_path('aplus_file') self.create_file(file_path, contents='old contents') self.assertTrue(self.os.path.exists(file_path)) with self.open(file_path, 'r') as fake_file: self.assertEqual('old contents', fake_file.read()) if self.filesystem: # need to recreate FakeFileOpen for OS specific initialization self.open = fake_filesystem.FakeFileOpen(self.filesystem, delete_on_close=True) with self.open(file_path, 'a+') as fake_file: self.assertEqual(12, fake_file.tell()) fake_file.write('new contents') self.assertEqual(24, fake_file.tell()) fake_file.seek(0) self.assertEqual('old contentsnew contents', fake_file.read()) def test_append_with_aplus_mac_os(self): self.check_macos_only() self.check_append_with_aplus() def test_append_with_aplus_linux_windows(self): self.check_linux_and_windows() self.check_append_with_aplus() def test_append_with_aplus_read_with_loop(self): # set up file_path = self.make_path('aplus_file') self.create_file(file_path, contents='old contents') self.assertTrue(self.os.path.exists(file_path)) with self.open(file_path, 'r') as fake_file: self.assertEqual('old contents', fake_file.read()) # actual tests with self.open(file_path, 'a+') as fake_file: fake_file.seek(0) fake_file.write('new contents') fake_file.seek(0) for line in fake_file: self.assertEqual('old contentsnew contents', line) def test_read_empty_file_with_aplus(self): file_path = self.make_path('aplus_file') with self.open(file_path, 'a+') as fake_file: self.assertEqual('', fake_file.read()) def test_read_with_rplus(self): # set up file_path = self.make_path('rplus_file') self.create_file(file_path, contents='old contents here') self.assertTrue(self.os.path.exists(file_path)) with self.open(file_path, 'r') as fake_file: self.assertEqual('old contents here', fake_file.read()) # actual tests with self.open(file_path, 'r+') as fake_file: self.assertEqual('old contents here', fake_file.read()) fake_file.seek(0) fake_file.write('new contents') fake_file.seek(0) self.assertEqual('new contents here', fake_file.read()) def create_with_permission(self, file_path, perm_bits): self.create_file(file_path) self.os.chmod(file_path, perm_bits) if perm_bits & PERM_READ: st = self.os.stat(file_path) self.assert_mode_equal(perm_bits, st.st_mode) self.assertTrue(st.st_mode & stat.S_IFREG) self.assertFalse(st.st_mode & stat.S_IFDIR) def test_open_flags700(self): # set up self.check_posix_only() file_path = self.make_path('target_file') self.create_with_permission(file_path, 0o700) # actual tests self.open(file_path, 'r').close() self.open(file_path, 'w').close() self.open(file_path, 'w+').close() with self.assertRaises(ValueError): self.open(file_path, 'INV') def test_open_flags400(self): # set up self.check_posix_only() file_path = self.make_path('target_file') self.create_with_permission(file_path, 0o400) # actual tests self.open(file_path, 'r').close() if not is_root(): self.assert_raises_os_error( errno.EACCES, self.open, file_path, 'w') self.assert_raises_os_error( errno.EACCES, self.open, file_path, 'w+') else: self.open(file_path, 'w').close() self.open(file_path, 'w+').close() def test_open_flags200(self): # set up self.check_posix_only() file_path = self.make_path('target_file') self.create_with_permission(file_path, 0o200) # actual tests self.open(file_path, 'w').close() if not is_root(): with self.assertRaises(OSError): self.open(file_path, 'r') with self.assertRaises(OSError): self.open(file_path, 'w+') else: self.open(file_path, 'r').close() self.open(file_path, 'w+').close() def test_open_flags100(self): # set up self.check_posix_only() file_path = self.make_path('target_file') self.create_with_permission(file_path, 0o100) # actual tests if not is_root(): with self.assertRaises(OSError): self.open(file_path, 'r') with self.assertRaises(OSError): self.open(file_path, 'w') with self.assertRaises(OSError): self.open(file_path, 'w+') else: self.open(file_path, 'r').close() self.open(file_path, 'w').close() self.open(file_path, 'w+').close() def test_follow_link_read(self): self.skip_if_symlink_not_supported() link_path = self.make_path('foo', 'bar', 'baz') target = self.make_path('tarJAY') target_contents = 'real baz contents' self.create_file(target, contents=target_contents) self.create_symlink(link_path, target) self.assert_equal_paths(target, self.os.readlink(link_path)) fh = self.open(link_path, 'r') got_contents = fh.read() fh.close() self.assertEqual(target_contents, got_contents) def test_follow_link_write(self): self.skip_if_symlink_not_supported() link_path = self.make_path('foo', 'bar', 'TBD') target = self.make_path('tarJAY') target_contents = 'real baz contents' self.create_symlink(link_path, target) self.assertFalse(self.os.path.exists(target)) with self.open(link_path, 'w') as fh: fh.write(target_contents) with self.open(target, 'r') as fh: got_contents = fh.read() self.assertEqual(target_contents, got_contents) def test_follow_intra_path_link_write(self): # Test a link in the middle of of a file path. self.skip_if_symlink_not_supported() link_path = self.os.path.join( self.base_path, 'foo', 'build', 'local_machine', 'output', '1') target = self.make_path('tmp', 'output', '1') self.create_dir(self.make_path('tmp', 'output')) self.create_symlink(self.os.path.join( self.base_path, 'foo', 'build', 'local_machine'), self.make_path('tmp')) self.assertFalse(self.os.path.exists(link_path)) self.assertFalse(self.os.path.exists(target)) target_contents = 'real baz contents' with self.open(link_path, 'w') as fh: fh.write(target_contents) with self.open(target, 'r') as fh: got_contents = fh.read() self.assertEqual(target_contents, got_contents) def test_open_raises_on_symlink_loop(self): # Regression test for #274 self.check_posix_only() file_dir = self.make_path('foo') self.os.mkdir(file_dir) file_path = self.os.path.join(file_dir, 'baz') self.os.symlink(file_path, file_path) self.assert_raises_os_error(errno.ELOOP, self.open, file_path) def test_file_descriptors_for_different_files(self): first_path = self.make_path('some_file1') self.create_file(first_path, contents='contents here1') second_path = self.make_path('some_file2') self.create_file(second_path, contents='contents here2') third_path = self.make_path('some_file3') self.create_file(third_path, contents='contents here3') with self.open(first_path) as fake_file1: with self.open(second_path) as fake_file2: with self.open(third_path) as fake_file3: fileno2 = fake_file2.fileno() self.assertGreater(fileno2, fake_file1.fileno()) self.assertGreater(fake_file3.fileno(), fileno2) def test_file_descriptors_for_the_same_file_are_different(self): first_path = self.make_path('some_file1') self.create_file(first_path, contents='contents here1') second_path = self.make_path('some_file2') self.create_file(second_path, contents='contents here2') with self.open(first_path) as fake_file1: with self.open(second_path) as fake_file2: with self.open(first_path) as fake_file1a: fileno2 = fake_file2.fileno() self.assertGreater(fileno2, fake_file1.fileno()) self.assertGreater(fake_file1a.fileno(), fileno2) def test_reused_file_descriptors_do_not_affect_others(self): first_path = self.make_path('some_file1') self.create_file(first_path, contents='contents here1') second_path = self.make_path('some_file2') self.create_file(second_path, contents='contents here2') third_path = self.make_path('some_file3') self.create_file(third_path, contents='contents here3') with self.open(first_path, 'r') as fake_file1: with self.open(second_path, 'r') as fake_file2: fake_file3 = self.open(third_path, 'r') fake_file1a = self.open(first_path, 'r') fileno1 = fake_file1.fileno() fileno2 = fake_file2.fileno() fileno3 = fake_file3.fileno() fileno4 = fake_file1a.fileno() with self.open(second_path, 'r') as fake_file2: with self.open(first_path, 'r') as fake_file1b: self.assertEqual(fileno1, fake_file2.fileno()) self.assertEqual(fileno2, fake_file1b.fileno()) self.assertEqual(fileno3, fake_file3.fileno()) self.assertEqual(fileno4, fake_file1a.fileno()) fake_file3.close() fake_file1a.close() def test_intertwined_read_write(self): file_path = self.make_path('some_file') self.create_file(file_path) with self.open(file_path, 'a') as writer: with self.open(file_path, 'r') as reader: writes = ['hello', 'world\n', 'somewhere\nover', 'the\n', 'rainbow'] reads = [] # when writes are flushes, they are piped to the reader for write in writes: writer.write(write) writer.flush() reads.append(reader.read()) reader.flush() self.assertEqual(writes, reads) writes = ['nothing', 'to\nsee', 'here'] reads = [] # when writes are not flushed, the reader doesn't read # anything new for write in writes: writer.write(write) reads.append(reader.read()) self.assertEqual(['' for _ in writes], reads) def test_intertwined_read_write_python3_str(self): file_path = self.make_path('some_file') self.create_file(file_path) with self.open(file_path, 'a', encoding='utf-8') as writer: with self.open(file_path, 'r', encoding='utf-8') as reader: writes = ['привет', 'мир\n', 'где-то\nза', 'радугой'] reads = [] # when writes are flushes, they are piped to the reader for write in writes: writer.write(write) writer.flush() reads.append(reader.read()) reader.flush() self.assertEqual(writes, reads) writes = ['ничего', 'не\nвидно'] reads = [] # when writes are not flushed, the reader doesn't # read anything new for write in writes: writer.write(write) reads.append(reader.read()) self.assertEqual(['' for _ in writes], reads) def test_open_io_errors(self): file_path = self.make_path('some_file') self.create_file(file_path) with self.open(file_path, 'a') as fh: with self.assertRaises(OSError): fh.read() with self.assertRaises(OSError): fh.readlines() with self.open(file_path, 'w') as fh: with self.assertRaises(OSError): fh.read() with self.assertRaises(OSError): fh.readlines() with self.open(file_path, 'r') as fh: with self.assertRaises(OSError): fh.truncate() with self.assertRaises(OSError): fh.write('contents') with self.assertRaises(OSError): fh.writelines(['con', 'tents']) def _iterator_open(mode): with self.open(file_path, mode) as f: for _ in f: pass with self.assertRaises(OSError): _iterator_open('w') with self.assertRaises(OSError): _iterator_open('a') def test_open_raises_io_error_if_parent_is_file_posix(self): self.check_posix_only() file_path = self.make_path('bar') self.create_file(file_path) file_path = self.os.path.join(file_path, 'baz') self.assert_raises_os_error(errno.ENOTDIR, self.open, file_path, 'w') def test_open_raises_io_error_if_parent_is_file_windows(self): self.check_windows_only() file_path = self.make_path('bar') self.create_file(file_path) file_path = self.os.path.join(file_path, 'baz') self.assert_raises_os_error(errno.ENOENT, self.open, file_path, 'w') def check_open_with_trailing_sep(self, error_nr): # regression test for #362 path = self.make_path('foo') + self.os.path.sep self.assert_raises_os_error(error_nr, self.open, path, 'w') def test_open_with_trailing_sep_linux(self): self.check_linux_only() self.check_open_with_trailing_sep(errno.EISDIR) def test_open_with_trailing_sep_macos(self): self.check_macos_only() self.check_open_with_trailing_sep(errno.ENOENT) def test_open_with_trailing_sep_windows(self): self.check_windows_only() self.check_open_with_trailing_sep(errno.EINVAL) def test_can_read_from_block_device(self): self.skip_real_fs() device_path = 'device' self.filesystem.create_file(device_path, stat.S_IFBLK | fake_filesystem.PERM_ALL) with self.open(device_path, 'r') as fh: self.assertEqual('', fh.read()) def test_truncate_flushes_contents(self): # Regression test for #285 file_path = self.make_path('baz') self.create_file(file_path) with self.open(file_path, 'w') as f0: f0.write('test') f0.truncate() self.assertEqual(4, self.os.path.getsize(file_path)) def test_update_other_instances_of_same_file_on_flush(self): # Regression test for #302 file_path = self.make_path('baz') with self.open(file_path, 'w') as f0: with self.open(file_path, 'w') as f1: f0.write('test') f0.truncate() f1.flush() self.assertEqual(4, self.os.path.getsize(file_path)) def test_getsize_after_truncate(self): # Regression test for #412 file_path = self.make_path('foo') with self.open(file_path, 'a') as f: f.write('a') f.seek(0) f.truncate() f.write('b') f.truncate() self.assertEqual(1, self.os.path.getsize(file_path)) self.assertEqual(1, self.os.stat(file_path).st_size) def test_st_size_after_truncate(self): # Regression test for #412 file_path = self.make_path('foo') with self.open(file_path, 'a') as f: f.write('a') f.truncate() f.write('b') f.truncate() self.assertEqual(2, self.os.stat(file_path).st_size) def test_that_read_over_end_does_not_reset_position(self): # Regression test for #286 file_path = self.make_path('baz') self.create_file(file_path) with self.open(file_path) as f0: f0.seek(2) f0.read() self.assertEqual(2, f0.tell()) def test_accessing_closed_file_raises(self): # Regression test for #275, #280 if self.is_pypy: raise unittest.SkipTest('Different exceptions with PyPy') file_path = self.make_path('foo') self.create_file(file_path, contents=b'test') fake_file = self.open(file_path, 'r') fake_file.close() with self.assertRaises(ValueError): fake_file.read(1) with self.assertRaises(ValueError): fake_file.write('a') with self.assertRaises(ValueError): fake_file.readline() with self.assertRaises(ValueError): fake_file.truncate() with self.assertRaises(ValueError): fake_file.tell() with self.assertRaises(ValueError): fake_file.seek(1) with self.assertRaises(ValueError): fake_file.flush() def test_accessing_open_file_with_another_handle_raises(self): # Regression test for #282 if self.is_pypy: raise unittest.SkipTest('Different exceptions with PyPy') file_path = self.make_path('foo') f0 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) fake_file = self.open(file_path, 'r') fake_file.close() with self.assertRaises(ValueError): fake_file.read(1) with self.assertRaises(ValueError): fake_file.write('a') self.os.close(f0) def test_tell_flushes_under_mac_os(self): # Regression test for #288 self.check_macos_only() file_path = self.make_path('foo') with self.open(file_path, 'w') as f0: f0.write('test') self.assertEqual(4, f0.tell()) self.assertEqual(4, self.os.path.getsize(file_path)) def test_tell_flushes_in_python3(self): # Regression test for #288 self.check_linux_and_windows() file_path = self.make_path('foo') with self.open(file_path, 'w') as f0: f0.write('test') self.assertEqual(4, f0.tell()) self.assertEqual(4, self.os.path.getsize(file_path)) def test_read_flushes_under_posix(self): # Regression test for #278 self.check_posix_only() file_path = self.make_path('foo') with self.open(file_path, 'a+') as f0: f0.write('test') self.assertEqual('', f0.read()) self.assertEqual(4, self.os.path.getsize(file_path)) def test_read_flushes_under_windows_in_python3(self): # Regression test for #278 self.check_windows_only() file_path = self.make_path('foo') with self.open(file_path, 'w+') as f0: f0.write('test') f0.read() self.assertEqual(4, self.os.path.getsize(file_path)) def test_seek_flushes(self): # Regression test for #290 file_path = self.make_path('foo') with self.open(file_path, 'w') as f0: f0.write('test') self.assertEqual(0, self.os.path.getsize(file_path)) f0.seek(3) self.assertEqual(4, self.os.path.getsize(file_path)) def test_truncate_flushes(self): # Regression test for #291 file_path = self.make_path('foo') with self.open(file_path, 'a') as f0: f0.write('test') self.assertEqual(0, self.os.path.getsize(file_path)) f0.truncate() self.assertEqual(4, self.os.path.getsize(file_path)) def check_seek_outside_and_truncate_sets_size(self, mode): # Regression test for #294 and #296 file_path = self.make_path('baz') with self.open(file_path, mode) as f0: f0.seek(1) f0.truncate() self.assertEqual(1, f0.tell()) self.assertEqual(1, self.os.path.getsize(file_path)) f0.seek(1) self.assertEqual(1, self.os.path.getsize(file_path)) self.assertEqual(1, self.os.path.getsize(file_path)) def test_seek_outside_and_truncate_sets_size_in_write_mode(self): # Regression test for #294 self.check_seek_outside_and_truncate_sets_size('w') def test_seek_outside_and_truncate_sets_size_in_append_mode(self): # Regression test for #295 self.check_seek_outside_and_truncate_sets_size('a') def test_closed(self): file_path = self.make_path('foo') f = self.open(file_path, 'w') self.assertFalse(f.closed) f.close() self.assertTrue(f.closed) f = self.open(file_path) self.assertFalse(f.closed) f.close() self.assertTrue(f.closed) def test_closing_closed_file_does_nothing(self): # Regression test for #299 file_path = self.make_path('baz') f0 = self.open(file_path, 'w') f0.close() with self.open(file_path) as f1: # would close f1 if not handled f0.close() self.assertEqual('', f1.read()) def test_closing_file_with_different_close_mode(self): self.skip_real_fs() filename = self.make_path('test.txt') fd = self.os.open(filename, os.O_CREAT | os.O_RDWR) file_obj = self.filesystem.get_object(filename) with self.open(fd, 'wb', closefd=False) as fp: fp.write(b'test') self.assertTrue(self.filesystem.has_open_file(file_obj)) self.os.close(fd) self.assertFalse(self.filesystem.has_open_file(file_obj)) def test_truncate_flushes_zeros(self): # Regression test for #301 file_path = self.make_path('baz') with self.open(file_path, 'w') as f0: with self.open(file_path) as f1: f0.seek(1) f0.truncate() self.assertEqual('\0', f1.read()) def test_byte_filename(self): file_path = self.make_path(b'test') with self.open(file_path, 'wb') as f: f.write(b'test') with self.open(file_path, 'rb') as f: self.assertEqual(b'test', f.read()) def test_unicode_filename(self): file_path = self.make_path('теÑÑ‚') with self.open(file_path, 'wb') as f: f.write(b'test') with self.open(file_path, 'rb') as f: self.assertEqual(b'test', f.read()) def test_write_devnull(self): for mode in ('r+', 'w', 'w+', 'a', 'a+'): with self.open(self.os.devnull, mode) as f: f.write('test') with self.open(self.os.devnull) as f: self.assertEqual('', f.read()) def test_utf16_text(self): # regression test for #574 file_path = self.make_path('foo') with self.open(file_path, "w", encoding='utf-16') as f: assert f.write("1") == 1 with self.open(file_path, "a", encoding='utf-16') as f: assert f.write("2") == 1 with self.open(file_path, "r", encoding='utf-16') as f: text = f.read() assert text == "12" class RealFileOpenTest(FakeFileOpenTest): def use_real_fs(self): return True @unittest.skipIf(sys.version_info < (3, 8), 'open_code only present since Python 3.8') class FakeFilePatchedOpenCodeTest(FakeFileOpenTestBase): def setUp(self): super(FakeFilePatchedOpenCodeTest, self).setUp() if self.use_real_fs(): self.open_code = io.open_code else: self.filesystem.patch_open_code = PatchMode.ON self.open_code = self.fake_io_module.open_code def tearDown(self): if not self.use_real_fs(): self.filesystem.patch_open_code = False super(FakeFilePatchedOpenCodeTest, self).tearDown() def test_invalid_path(self): with self.assertRaises(TypeError): self.open_code(4) def test_byte_contents_open_code(self): byte_fractions = b'\xe2\x85\x93 \xe2\x85\x94 \xe2\x85\x95 \xe2\x85\x96' file_path = self.make_path('foo') self.create_file(file_path, contents=byte_fractions) with self.open_code(file_path) as f: contents = f.read() self.assertEqual(contents, byte_fractions) def test_open_code_in_real_fs(self): self.skip_real_fs() file_path = __file__ with self.assertRaises(OSError): self.open_code(file_path) class RealPatchedFileOpenCodeTest(FakeFilePatchedOpenCodeTest): def use_real_fs(self): return True @unittest.skipIf(sys.version_info < (3, 8), 'open_code only present since Python 3.8') class FakeFileUnpatchedOpenCodeTest(FakeFileOpenTestBase): def setUp(self): super(FakeFileUnpatchedOpenCodeTest, self).setUp() if self.use_real_fs(): self.open_code = io.open_code else: self.open_code = self.fake_io_module.open_code def test_invalid_path(self): with self.assertRaises(TypeError): self.open_code(4) def test_open_code_in_real_fs(self): file_path = __file__ with self.open_code(file_path) as f: contents = f.read() self.assertTrue(len(contents) > 100) class RealUnpatchedFileOpenCodeTest(FakeFileUnpatchedOpenCodeTest): def use_real_fs(self): return True def test_byte_contents_open_code(self): byte_fractions = b'\xe2\x85\x93 \xe2\x85\x94 \xe2\x85\x95 \xe2\x85\x96' file_path = self.make_path('foo') self.create_file(file_path, contents=byte_fractions) with self.open_code(file_path) as f: contents = f.read() self.assertEqual(contents, byte_fractions) class BufferingModeTest(FakeFileOpenTestBase): def test_no_buffering(self): file_path = self.make_path("buffertest.bin") with self.open(file_path, 'wb', buffering=0) as f: f.write(b'a' * 128) with self.open(file_path, "rb") as r: x = r.read() self.assertEqual(b'a' * 128, x) def test_no_buffering_not_allowed_in_textmode(self): file_path = self.make_path("buffertest.txt") with self.assertRaises(ValueError): self.open(file_path, 'w', buffering=0) def test_default_buffering_no_flush(self): file_path = self.make_path("buffertest.bin") with self.open(file_path, 'wb') as f: f.write(b'a' * 2048) with self.open(file_path, "rb") as r: x = r.read() self.assertEqual(b'', x) with self.open(file_path, "rb") as r: x = r.read() self.assertEqual(b'a' * 2048, x) def test_default_buffering_flush(self): file_path = self.make_path("buffertest.bin") with self.open(file_path, 'wb') as f: f.write(b'a' * 2048) f.flush() with self.open(file_path, "rb") as r: x = r.read() self.assertEqual(b'a' * 2048, x) def test_writing_with_specific_buffer(self): file_path = self.make_path("buffertest.bin") with self.open(file_path, 'wb', buffering=512) as f: f.write(b'a' * 500) with self.open(file_path, "rb") as r: x = r.read() # buffer not filled - not written self.assertEqual(0, len(x)) f.write(b'a' * 400) with self.open(file_path, "rb") as r: x = r.read() # buffer exceeded, but new buffer (400) not - previous written self.assertEqual(500, len(x)) f.write(b'a' * 100) with self.open(file_path, "rb") as r: x = r.read() # buffer not full (500) not written self.assertEqual(500, len(x)) f.write(b'a' * 100) with self.open(file_path, "rb") as r: x = r.read() # buffer exceeded (600) -> write previous # new buffer not full (100) - not written self.assertEqual(1000, len(x)) f.write(b'a' * 600) with self.open(file_path, "rb") as r: x = r.read() # new buffer exceeded (600) -> all written self.assertEqual(1700, len(x)) def test_writing_text_with_line_buffer(self): file_path = self.make_path("buffertest.bin") with self.open(file_path, 'w', buffering=1) as f: f.write('test' * 100) with self.open(file_path, "r") as r: x = r.read() # no new line - not written self.assertEqual(0, len(x)) f.write('\ntest') with self.open(file_path, "r") as r: x = r.read() # new line - buffer written self.assertEqual(405, len(x)) f.write('test' * 10) with self.open(file_path, "r") as r: x = r.read() # buffer not filled - not written self.assertEqual(405, len(x)) f.write('\ntest') with self.open(file_path, "r") as r: x = r.read() # new line - buffer written self.assertEqual(450, len(x)) def test_writing_large_text_with_line_buffer(self): file_path = self.make_path("buffertest.bin") with self.open(file_path, 'w', buffering=1) as f: f.write('test' * 4000) with self.open(file_path, "r") as r: x = r.read() # buffer larger than default - written self.assertEqual(16000, len(x)) f.write('test') with self.open(file_path, "r") as r: x = r.read() # buffer not filled - not written self.assertEqual(16000, len(x)) f.write('\ntest') with self.open(file_path, "r") as r: x = r.read() # new line - buffer written self.assertEqual(16009, len(x)) f.write('\ntest') with self.open(file_path, "r") as r: x = r.read() # another new line - buffer written self.assertEqual(16014, len(x)) def test_writing_text_with_default_buffer(self): file_path = self.make_path("buffertest.txt") with self.open(file_path, 'w') as f: f.write('test' * 5) with self.open(file_path, "r") as r: x = r.read() # buffer not filled - not written self.assertEqual(0, len(x)) f.write('\ntest') with self.open(file_path, "r") as r: x = r.read() # buffer exceeded, but new buffer (400) not - previous written self.assertEqual(0, len(x)) f.write('test' * 10) with self.open(file_path, "r") as r: x = r.read() # buffer not filled - not written self.assertEqual(0, len(x)) f.write('\ntest') with self.open(file_path, "r") as r: x = r.read() self.assertEqual(0, len(x)) def test_writing_text_with_specific_buffer(self): file_path = self.make_path("buffertest.txt") with self.open(file_path, 'w', buffering=2) as f: f.write('a' * 8000) with self.open(file_path, "r") as r: x = r.read() # buffer not filled - not written self.assertEqual(0, len(x)) f.write('test') with self.open(file_path, "r") as r: x = r.read() # buffer exceeded, but new buffer (400) not - previous written self.assertEqual(0, len(x)) f.write('test') with self.open(file_path, "r") as r: x = r.read() # buffer not filled - not written self.assertEqual(0, len(x)) f.write('test') with self.open(file_path, "r") as r: x = r.read() self.assertEqual(0, len(x)) # with self.open(file_path, "r") as r: # x = r.read() # self.assertEqual(35, len(x)) def test_append_with_specific_buffer(self): file_path = self.make_path("buffertest.bin") with self.open(file_path, 'wb', buffering=512) as f: f.write(b'a' * 500) with self.open(file_path, 'ab', buffering=512) as f: f.write(b'a' * 500) with self.open(file_path, "rb") as r: x = r.read() # buffer not filled - not written self.assertEqual(500, len(x)) f.write(b'a' * 400) with self.open(file_path, "rb") as r: x = r.read() # buffer exceeded, but new buffer (400) not - previous written self.assertEqual(1000, len(x)) f.write(b'a' * 100) with self.open(file_path, "rb") as r: x = r.read() # buffer not full (500) not written self.assertEqual(1000, len(x)) f.write(b'a' * 100) with self.open(file_path, "rb") as r: x = r.read() # buffer exceeded (600) -> write previous # new buffer not full (100) - not written self.assertEqual(1500, len(x)) f.write(b'a' * 600) with self.open(file_path, "rb") as r: x = r.read() # new buffer exceeded (600) -> all written self.assertEqual(2200, len(x)) def test_failed_flush_does_not_truncate_file(self): # regression test for #548 self.skip_real_fs() # cannot set fs size in real fs self.filesystem.set_disk_usage(100) self.os.makedirs("foo") file_path = self.os.path.join('foo', 'bar.txt') with self.open(file_path, 'wb') as f: f.write(b'a' * 50) f.flush() with self.open(file_path, "rb") as r: x = r.read() self.assertTrue(x.startswith(b'a' * 50)) with self.assertRaises(OSError): f.write(b'b' * 200) f.flush() with self.open(file_path, "rb") as r: x = r.read() self.assertTrue(x.startswith(b'a' * 50)) f.truncate(50) def test_failed_write_does_not_truncate_file(self): # test the same with no buffering and no flush self.skip_real_fs() # cannot set fs size in real fs self.filesystem.set_disk_usage(100) self.os.makedirs("foo") file_path = self.os.path.join('foo', 'bar.txt') with self.open(file_path, 'wb', buffering=0) as f: f.write(b'a' * 50) with self.open(file_path, "rb") as r: x = r.read() self.assertEqual(b'a' * 50, x) with self.assertRaises(OSError): f.write(b'b' * 200) with self.open(file_path, "rb") as r: x = r.read() self.assertEqual(b'a' * 50, x) class RealBufferingTest(BufferingModeTest): def use_real_fs(self): return True class OpenFileWithEncodingTest(FakeFileOpenTestBase): """Tests that are similar to some open file tests above but using an explicit text encoding.""" def setUp(self): super(OpenFileWithEncodingTest, self).setUp() self.file_path = self.make_path('foo') def test_write_str_read_bytes(self): str_contents = u'علي بابا' with self.open(self.file_path, 'w', encoding='arabic') as f: f.write(str_contents) with self.open(self.file_path, 'rb') as f: contents = f.read() self.assertEqual(str_contents, contents.decode('arabic')) def test_write_str_error_modes(self): str_contents = u'علي بابا' with self.open(self.file_path, 'w', encoding='cyrillic') as f: with self.assertRaises(UnicodeEncodeError): f.write(str_contents) with self.open(self.file_path, 'w', encoding='ascii', errors='xmlcharrefreplace') as f: f.write(str_contents) with self.open(self.file_path, 'r', encoding='ascii') as f: contents = f.read() self.assertEqual('علي بابا', contents) with self.open(self.file_path, 'w', encoding='ascii', errors='namereplace') as f: f.write(str_contents) with self.open(self.file_path, 'r', encoding='ascii') as f: contents = f.read() self.assertEqual( r'\N{ARABIC LETTER AIN}\N{ARABIC LETTER LAM}\N' r'{ARABIC LETTER YEH} \N{ARABIC LETTER BEH}\N' r'{ARABIC LETTER ALEF}\N{ARABIC LETTER BEH}' r'\N{ARABIC LETTER ALEF}', contents) def test_read_str_error_modes(self): str_contents = u'علي بابا' with self.open(self.file_path, 'w', encoding='arabic') as f: f.write(str_contents) # default strict encoding with self.open(self.file_path, encoding='ascii') as f: with self.assertRaises(UnicodeDecodeError): f.read() with self.open(self.file_path, encoding='ascii', errors='replace') as f: contents = f.read() self.assertNotEqual(str_contents, contents) with self.open(self.file_path, encoding='ascii', errors='backslashreplace') as f: contents = f.read() self.assertEqual(r'\xd9\xe4\xea \xc8\xc7\xc8\xc7', contents) def test_write_and_read_str(self): str_contents = u'علي بابا' with self.open(self.file_path, 'w', encoding='arabic') as f: f.write(str_contents) with self.open(self.file_path, 'r', encoding='arabic') as f: contents = f.read() self.assertEqual(str_contents, contents) def test_create_file_with_append(self): contents = [ u'Allons enfants de la Patrie,' u'Le jour de gloire est arrivé!', u'Contre nous de la tyrannie,', u'L’étendard sanglant est levé.', ] with self.open(self.file_path, 'a', encoding='utf-8') as fake_file: for line in contents: fake_file.write(line + '\n') with self.open(self.file_path, encoding='utf-8') as fake_file: result = [line.rstrip() for line in fake_file] self.assertEqual(contents, result) def test_append_existing_file(self): contents = [ u'Оригинальное Ñодержание' u'Дополнительное Ñодержание', ] self.create_file(self.file_path, contents=contents[0], encoding='cyrillic') with self.open(self.file_path, 'a', encoding='cyrillic') as fake_file: for line in contents[1:]: fake_file.write(line + '\n') with self.open(self.file_path, encoding='cyrillic') as fake_file: result = [line.rstrip() for line in fake_file] self.assertEqual(contents, result) def test_open_with_wplus(self): self.create_file(self.file_path, contents=u'Ñтарое Ñодержание', encoding='cyrillic') with self.open(self.file_path, 'r', encoding='cyrillic') as fake_file: self.assertEqual(u'Ñтарое Ñодержание', fake_file.read()) with self.open(self.file_path, 'w+', encoding='cyrillic') as fake_file: fake_file.write(u'новое Ñодержание') fake_file.seek(0) self.assertTrue(u'новое Ñодержание', fake_file.read()) def test_open_with_append_flag(self): contents = [ u'Калинка,\n', u'калинка,\n', u'калинка моÑ,\n' ] additional_contents = [ u'Ð’ Ñаду Ñгода-малинка,\n', u'малинка моÑ.\n' ] self.create_file(self.file_path, contents=''.join(contents), encoding='cyrillic') with self.open(self.file_path, 'a', encoding='cyrillic') as fake_file: with self.assertRaises(io.UnsupportedOperation): fake_file.read(0) with self.assertRaises(io.UnsupportedOperation): fake_file.readline() self.assertEqual(len(''.join(contents)), fake_file.tell()) fake_file.seek(0) self.assertEqual(0, fake_file.tell()) fake_file.writelines(additional_contents) with self.open(self.file_path, encoding='cyrillic') as fake_file: self.assertEqual(contents + additional_contents, fake_file.readlines()) def test_append_with_aplus(self): self.create_file(self.file_path, contents=u'Ñтарое Ñодержание', encoding='cyrillic') fake_file = self.open(self.file_path, 'r', encoding='cyrillic') fake_file.close() with self.open(self.file_path, 'a+', encoding='cyrillic') as fake_file: self.assertEqual(17, fake_file.tell()) fake_file.write(u'новое Ñодержание') self.assertEqual(33, fake_file.tell()) fake_file.seek(0) self.assertEqual(u'Ñтарое Ñодержаниеновое Ñодержание', fake_file.read()) def test_read_with_rplus(self): self.create_file(self.file_path, contents=u'Ñтарое Ñодержание здеÑÑŒ', encoding='cyrillic') fake_file = self.open(self.file_path, 'r', encoding='cyrillic') fake_file.close() with self.open(self.file_path, 'r+', encoding='cyrillic') as fake_file: self.assertEqual(u'Ñтарое Ñодержание здеÑÑŒ', fake_file.read()) fake_file.seek(0) fake_file.write(u'новое Ñодержание') fake_file.seek(0) self.assertEqual(u'новое Ñодержание здеÑÑŒ', fake_file.read()) class OpenRealFileWithEncodingTest(OpenFileWithEncodingTest): def use_real_fs(self): return True class FakeFileOpenLineEndingTest(FakeFileOpenTestBase): def setUp(self): super(FakeFileOpenLineEndingTest, self).setUp() def test_read_default_newline_mode(self): file_path = self.make_path('some_file') for contents in (b'1\n2', b'1\r\n2', b'1\r2'): self.create_file(file_path, contents=contents) with self.open(file_path, mode='r') as f: self.assertEqual(['1\n', '2'], f.readlines()) with self.open(file_path, mode='r') as f: self.assertEqual('1\n2', f.read()) with self.open(file_path, mode='rb') as f: self.assertEqual(contents, f.read()) def test_write_universal_newline_mode(self): file_path = self.make_path('some_file') with self.open(file_path, 'w') as f: f.write('1\n2') with self.open(file_path, mode='rb') as f: self.assertEqual(b'1' + self.os.linesep.encode() + b'2', f.read()) with self.open(file_path, 'w') as f: f.write('1\r\n2') with self.open(file_path, mode='rb') as f: self.assertEqual(b'1\r' + self.os.linesep.encode() + b'2', f.read()) def test_read_with_newline_arg(self): file_path = self.make_path('some_file') file_contents = b'1\r\n2\n3\r4' self.create_file(file_path, contents=file_contents) with self.open(file_path, mode='r', newline='') as f: self.assertEqual('1\r\n2\n3\r4', f.read()) with self.open(file_path, mode='r', newline='\r') as f: self.assertEqual('1\r\n2\n3\r4', f.read()) with self.open(file_path, mode='r', newline='\n') as f: self.assertEqual('1\r\n2\n3\r4', f.read()) with self.open(file_path, mode='r', newline='\r\n') as f: self.assertEqual('1\r\n2\n3\r4', f.read()) def test_readlines_with_newline_arg(self): file_path = self.make_path('some_file') file_contents = b'1\r\n2\n3\r4' self.create_file(file_path, contents=file_contents) with self.open(file_path, mode='r', newline='') as f: self.assertEqual(['1\r\n', '2\n', '3\r', '4'], f.readlines()) with self.open(file_path, mode='r', newline='\r') as f: self.assertEqual(['1\r', '\n2\n3\r', '4'], f.readlines()) with self.open(file_path, mode='r', newline='\n') as f: self.assertEqual(['1\r\n', '2\n', '3\r4'], f.readlines()) with self.open(file_path, mode='r', newline='\r\n') as f: self.assertEqual(['1\r\n', '2\n3\r4'], f.readlines()) def test_read_with_ignored_universal_newlines_flag(self): file_path = self.make_path('some_file') file_contents = b'1\r\n2\n3\r4' self.create_file(file_path, contents=file_contents) with self.open(file_path, mode='r', newline='\r') as f: self.assertEqual('1\r\n2\n3\r4', f.read()) with self.open(file_path, mode='r', newline='\r') as f: self.assertEqual('1\r\n2\n3\r4', f.read()) with self.open(file_path, mode='U', newline='\r') as f: self.assertEqual('1\r\n2\n3\r4', f.read()) def test_write_with_newline_arg(self): file_path = self.make_path('some_file') with self.open(file_path, 'w', newline='') as f: f.write('1\r\n2\n3\r4') with self.open(file_path, mode='rb') as f: self.assertEqual(b'1\r\n2\n3\r4', f.read()) with self.open(file_path, 'w', newline='\n') as f: f.write('1\r\n2\n3\r4') with self.open(file_path, mode='rb') as f: self.assertEqual(b'1\r\n2\n3\r4', f.read()) with self.open(file_path, 'w', newline='\r\n') as f: f.write('1\r\n2\n3\r4') with self.open(file_path, mode='rb') as f: self.assertEqual(b'1\r\r\n2\r\n3\r4', f.read()) with self.open(file_path, 'w', newline='\r') as f: f.write('1\r\n2\n3\r4') with self.open(file_path, mode='rb') as f: self.assertEqual(b'1\r\r2\r3\r4', f.read()) def test_binary_readline(self): file_path = self.make_path('some_file') file_contents = b'\x80\n\x80\r\x80\r\n\x80' def chunk_line(): px = 0 while px < len(file_contents): ix = file_contents.find(b'\n', px) if ix == -1: yield file_contents[px:] return yield file_contents[px:ix + 1] px = ix + 1 chunked_contents = list(chunk_line()) self.create_file(file_path, contents=file_contents) with self.open(file_path, mode='rb') as f: self.assertEqual(chunked_contents, list(f)) class RealFileOpenLineEndingTest(FakeFileOpenLineEndingTest): def use_real_fs(self): return True class FakeFileOpenLineEndingWithEncodingTest(FakeFileOpenTestBase): def setUp(self): super(FakeFileOpenLineEndingWithEncodingTest, self).setUp() def test_read_standard_newline_mode(self): file_path = self.make_path('some_file') for contents in (u'раз\nдва', u'раз\r\nдва', u'раз\rдва'): self.create_file(file_path, contents=contents, encoding='cyrillic') with self.open(file_path, mode='r', encoding='cyrillic') as fake_file: self.assertEqual([u'раз\n', u'два'], fake_file.readlines()) with self.open(file_path, mode='r', encoding='cyrillic') as fake_file: self.assertEqual(u'раз\nдва', fake_file.read()) def test_write_universal_newline_mode(self): file_path = self.make_path('some_file') with self.open(file_path, 'w', encoding='cyrillic') as f: f.write(u'раз\nдва') with self.open(file_path, mode='rb') as f: self.assertEqual(u'раз'.encode('cyrillic') + self.os.linesep.encode() + u'два'.encode('cyrillic'), f.read()) with self.open(file_path, 'w', encoding='cyrillic') as f: f.write(u'раз\r\nдва') with self.open(file_path, mode='rb') as f: self.assertEqual(u'раз\r'.encode('cyrillic') + self.os.linesep.encode() + u'два'.encode('cyrillic'), f.read()) def test_read_with_newline_arg(self): file_path = self.make_path('some_file') file_contents = u'раз\r\nдва\nтри\rчетыре' self.create_file(file_path, contents=file_contents, encoding='cyrillic') with self.open(file_path, mode='r', newline='', encoding='cyrillic') as f: self.assertEqual(u'раз\r\nдва\nтри\rчетыре', f.read()) with self.open(file_path, mode='r', newline='\r', encoding='cyrillic') as f: self.assertEqual(u'раз\r\nдва\nтри\rчетыре', f.read()) with self.open(file_path, mode='r', newline='\n', encoding='cyrillic') as f: self.assertEqual(u'раз\r\nдва\nтри\rчетыре', f.read()) with self.open(file_path, mode='r', newline='\r\n', encoding='cyrillic') as f: self.assertEqual(u'раз\r\nдва\nтри\rчетыре', f.read()) def test_readlines_with_newline_arg(self): file_path = self.make_path('some_file') file_contents = u'раз\r\nдва\nтри\rчетыре' self.create_file(file_path, contents=file_contents, encoding='cyrillic') with self.open(file_path, mode='r', newline='', encoding='cyrillic') as f: self.assertEqual([u'раз\r\n', u'два\n', u'три\r', u'четыре'], f.readlines()) with self.open(file_path, mode='r', newline='\r', encoding='cyrillic') as f: self.assertEqual([u'раз\r', u'\nдва\nтри\r', u'четыре'], f.readlines()) with self.open(file_path, mode='r', newline='\n', encoding='cyrillic') as f: self.assertEqual([u'раз\r\n', u'два\n', u'три\rчетыре'], f.readlines()) with self.open(file_path, mode='r', newline='\r\n', encoding='cyrillic') as f: self.assertEqual([u'раз\r\n', u'два\nтри\rчетыре'], f.readlines()) def test_write_with_newline_arg(self): file_path = self.make_path('some_file') with self.open(file_path, 'w', newline='', encoding='cyrillic') as f: f.write(u'раз\r\nдва\nтри\rчетыре') with self.open(file_path, mode='rb') as f: self.assertEqual(u'раз\r\nдва\nтри\rчетыре'.encode('cyrillic'), f.read()) with self.open(file_path, 'w', newline='\n', encoding='cyrillic') as f: f.write('раз\r\nдва\nтри\rчетыре') with self.open(file_path, mode='rb') as f: self.assertEqual(u'раз\r\nдва\nтри\rчетыре'.encode('cyrillic'), f.read()) with self.open(file_path, 'w', newline='\r\n', encoding='cyrillic') as f: f.write('раз\r\nдва\nтри\rчетыре') with self.open(file_path, mode='rb') as f: self.assertEqual(u'раз\r\r\nдва\r\nтри\rчетыре'.encode('cyrillic'), f.read()) with self.open(file_path, 'w', newline='\r', encoding='cyrillic') as f: f.write('раз\r\nдва\nтри\rчетыре') with self.open(file_path, mode='rb') as f: self.assertEqual(u'раз\r\rдва\rтри\rчетыре'.encode('cyrillic'), f.read()) class RealFileOpenLineEndingWithEncodingTest( FakeFileOpenLineEndingWithEncodingTest): def use_real_fs(self): return True class OpenWithFileDescriptorTest(FakeFileOpenTestBase): def test_open_with_file_descriptor(self): file_path = self.make_path('this', 'file') self.create_file(file_path) fd = self.os.open(file_path, os.O_CREAT) self.assertEqual(fd, self.open(fd, 'r').fileno()) def test_closefd_with_file_descriptor(self): file_path = self.make_path('this', 'file') self.create_file(file_path) fd = self.os.open(file_path, os.O_CREAT) fh = self.open(fd, 'r', closefd=False) fh.close() self.assertIsNotNone(self.filesystem.open_files[fd]) fh = self.open(fd, 'r', closefd=True) fh.close() self.assertIsNone(self.filesystem.open_files[fd]) class OpenWithRealFileDescriptorTest(FakeFileOpenTestBase): def use_real_fs(self): return True class OpenWithFlagsTestBase(FakeFileOpenTestBase): def setUp(self): super(OpenWithFlagsTestBase, self).setUp() self.file_path = self.make_path('some_file') self.file_contents = None def open_file(self, mode): return self.open(self.file_path, mode=mode) def open_file_and_seek(self, mode): fake_file = self.open(self.file_path, mode=mode) fake_file.seek(0, 2) return fake_file def write_and_reopen_file(self, fake_file, mode='r', encoding=None): fake_file.write(self.file_contents) fake_file.close() args = {'mode': mode} if encoding: args['encoding'] = encoding return self.open(self.file_path, **args) class OpenWithBinaryFlagsTest(OpenWithFlagsTestBase): def setUp(self): super(OpenWithBinaryFlagsTest, self).setUp() self.file_contents = b'real binary contents: \x1f\x8b' self.create_file(self.file_path, contents=self.file_contents) def test_read_binary(self): with self.open_file('rb') as fake_file: self.assertEqual(self.file_contents, fake_file.read()) def test_write_binary(self): with self.open_file_and_seek('wb') as f: self.assertEqual(0, f.tell()) with self.write_and_reopen_file(f, mode='rb') as f1: self.assertEqual(self.file_contents, f1.read()) # Attempt to reopen the file in text mode with self.open_file('wb') as f2: with self.write_and_reopen_file(f2, mode='r', encoding='ascii') as f3: with self.assertRaises(UnicodeDecodeError): f3.read() def test_write_and_read_binary(self): with self.open_file_and_seek('w+b') as f: self.assertEqual(0, f.tell()) with self.write_and_reopen_file(f, mode='rb') as f1: self.assertEqual(self.file_contents, f1.read()) class RealOpenWithBinaryFlagsTest(OpenWithBinaryFlagsTest): def use_real_fs(self): return True class OpenWithTextModeFlagsTest(OpenWithFlagsTestBase): def setUp(self): super(OpenWithTextModeFlagsTest, self).setUp() self.setUpFileSystem() def setUpFileSystem(self): self.file_path = self.make_path('some_file') self.file_contents = b'two\r\nlines' self.original_contents = 'two\r\nlines' self.converted_contents = 'two\nlines' self.create_file(self.file_path, contents=self.file_contents) def test_read_text(self): """Test that text mode flag is ignored""" self.check_windows_only() with self.open_file('r') as f: self.assertEqual(self.converted_contents, f.read()) with self.open_file('rt') as f: self.assertEqual(self.converted_contents, f.read()) def test_mixed_text_and_binary_flags(self): with self.assertRaises(ValueError): self.open_file_and_seek('w+bt') class RealOpenWithTextModeFlagsTest(OpenWithTextModeFlagsTest): def use_real_fs(self): return True class OpenWithInvalidFlagsTest(FakeFileOpenTestBase): def test_capital_r(self): with self.assertRaises(ValueError): self.open('some_file', 'R') def test_capital_w(self): with self.assertRaises(ValueError): self.open('some_file', 'W') def test_capital_a(self): with self.assertRaises(ValueError): self.open('some_file', 'A') def test_lower_u(self): with self.assertRaises(ValueError): self.open('some_file', 'u') def test_lower_rw(self): with self.assertRaises(ValueError): self.open('some_file', 'rw') class OpenWithInvalidFlagsRealFsTest(OpenWithInvalidFlagsTest): def use_real_fs(self): return True class ResolvePathTest(FakeFileOpenTestBase): def write_to_file(self, file_name): with self.open(file_name, 'w') as fh: fh.write('x') def test_none_filepath_raises_type_error(self): with self.assertRaises(TypeError): self.open(None, 'w') def test_empty_filepath_raises_io_error(self): with self.assertRaises(OSError): self.open('', 'w') def test_normal_path(self): file_path = self.make_path('foo') self.write_to_file(file_path) self.assertTrue(self.os.path.exists(file_path)) def test_link_within_same_directory(self): self.skip_if_symlink_not_supported() final_target = self.make_path('foo', 'baz') link_path = self.make_path('foo', 'bar') self.create_symlink(link_path, 'baz') self.write_to_file(link_path) self.assertTrue(self.os.path.exists(final_target)) self.assertEqual(1, self.os.stat(final_target)[stat.ST_SIZE]) def test_link_to_sub_directory(self): self.skip_if_symlink_not_supported() final_target = self.make_path('foo', 'baz', 'bip') dir_path = self.make_path('foo', 'baz') self.create_dir(dir_path) link_path = self.make_path('foo', 'bar') target_path = self.os.path.join('baz', 'bip') self.create_symlink(link_path, target_path) self.write_to_file(link_path) self.assertTrue(self.os.path.exists(final_target)) self.assertEqual(1, self.os.stat(final_target)[stat.ST_SIZE]) self.assertTrue(self.os.path.exists(dir_path)) # Make sure that intermediate directory got created. self.assertTrue(self.os.stat(dir_path)[stat.ST_MODE] & stat.S_IFDIR) def test_link_to_parent_directory(self): self.skip_if_symlink_not_supported() final_target = self.make_path('baz', 'bip') self.create_dir(self.make_path('foo')) self.create_dir(self.make_path('baz')) link_path = self.make_path('foo', 'bar') self.create_symlink(link_path, self.os.path.join('..', 'baz')) self.write_to_file(self.make_path('foo', 'bar', 'bip')) self.assertTrue(self.os.path.exists(final_target)) self.assertEqual(1, self.os.stat(final_target)[stat.ST_SIZE]) self.assertTrue(self.os.path.exists(link_path)) def test_link_to_absolute_path(self): self.skip_if_symlink_not_supported() final_target = self.make_path('foo', 'baz', 'bip') self.create_dir(self.make_path('foo', 'baz')) link_path = self.make_path('foo', 'bar') self.create_symlink(link_path, final_target) self.write_to_file(link_path) self.assertTrue(self.os.path.exists(final_target)) def test_relative_links_work_after_chdir(self): self.skip_if_symlink_not_supported() final_target = self.make_path('foo', 'baz', 'bip') self.create_dir(self.make_path('foo', 'baz')) link_path = self.make_path('foo', 'bar') self.create_symlink(link_path, self.os.path.join('.', 'baz', 'bip')) if not self.is_windows: self.assert_equal_paths( final_target, self.os.path.realpath(link_path)) self.assertTrue(self.os.path.islink(link_path)) self.os.chdir(self.make_path('foo')) self.assert_equal_paths(self.make_path('foo'), self.os.getcwd()) self.assertTrue(self.os.path.islink('bar')) if not self.is_windows: self.assert_equal_paths(final_target, self.os.path.realpath('bar')) self.write_to_file(link_path) self.assertTrue(self.os.path.exists(final_target)) def test_absolute_links_work_after_chdir(self): self.skip_if_symlink_not_supported() final_target = self.make_path('foo', 'baz', 'bip') self.create_dir(self.make_path('foo', 'baz')) link_path = self.make_path('foo', 'bar') self.create_symlink(link_path, final_target) if not self.is_windows: self.assert_equal_paths( final_target, self.os.path.realpath(link_path)) self.assertTrue(self.os.path.islink(link_path)) self.os.chdir(self.make_path('foo')) self.assert_equal_paths(self.make_path('foo'), self.os.getcwd()) self.assertTrue(self.os.path.islink('bar')) if not self.is_windows: self.assert_equal_paths(final_target, self.os.path.realpath('bar')) self.write_to_file(link_path) self.assertTrue(self.os.path.exists(final_target)) def test_chdir_through_relative_link(self): self.check_posix_only() dir1_path = self.make_path('x', 'foo') dir2_path = self.make_path('x', 'bar') self.create_dir(dir1_path) self.create_dir(dir2_path) link_path = self.make_path('x', 'foo', 'bar') self.create_symlink(link_path, self.os.path.join('..', 'bar')) self.assert_equal_paths(dir2_path, self.os.path.realpath(link_path)) self.os.chdir(dir1_path) self.assert_equal_paths(dir1_path, self.os.getcwd()) self.assert_equal_paths(dir2_path, self.os.path.realpath('bar')) self.os.chdir('bar') self.assert_equal_paths(dir2_path, self.os.getcwd()) def test_chdir_uses_open_fd_as_path(self): self.check_posix_only() if self.is_pypy: # unclear behavior with PyPi self.skip_real_fs() self.assert_raises_os_error( [errno.ENOTDIR, errno.EBADF], self.os.chdir, 500) dir_path = self.make_path('foo', 'bar') self.create_dir(dir_path) path_des = self.os.open(dir_path, os.O_RDONLY) self.os.chdir(path_des) self.os.close(path_des) self.assert_equal_paths(dir_path, self.os.getcwd()) def test_read_link_to_link(self): # Write into the final link target and read back from a file which will # point to that. self.skip_if_symlink_not_supported() link_path = self.make_path('foo', 'bar') self.create_symlink(link_path, 'link') self.create_symlink(self.make_path('foo', 'link'), 'baz') self.write_to_file(self.make_path('foo', 'baz')) fh = self.open(link_path, 'r') self.assertEqual('x', fh.read()) def test_write_link_to_link(self): self.skip_if_symlink_not_supported() final_target = self.make_path('foo', 'baz') link_path = self.make_path('foo', 'bar') self.create_symlink(link_path, 'link') self.create_symlink(self.make_path('foo', 'link'), 'baz') self.write_to_file(link_path) self.assertTrue(self.os.path.exists(final_target)) def test_multiple_links(self): self.skip_if_symlink_not_supported() self.os.makedirs(self.make_path('a', 'link1', 'c', 'link2')) self.create_symlink(self.make_path('a', 'b'), 'link1') if not self.is_windows: self.assert_equal_paths(self.make_path('a', 'link1'), self.os.path.realpath( self.make_path('a', 'b'))) self.assert_equal_paths(self.make_path('a', 'link1', 'c'), self.os.path.realpath( self.make_path('a', 'b', 'c'))) link_path = self.make_path('a', 'link1', 'c', 'd') self.create_symlink(link_path, 'link2') self.assertTrue(self.os.path.exists(link_path)) self.assertTrue(self.os.path.exists( self.make_path('a', 'b', 'c', 'd'))) final_target = self.make_path('a', 'link1', 'c', 'link2', 'e') self.assertFalse(self.os.path.exists(final_target)) self.write_to_file(self.make_path('a', 'b', 'c', 'd', 'e')) self.assertTrue(self.os.path.exists(final_target)) def test_utime_link(self): """os.utime() and os.stat() via symbolic link (issue #49)""" self.skip_if_symlink_not_supported() self.create_dir(self.make_path('foo', 'baz')) target_path = self.make_path('foo', 'baz', 'bip') self.write_to_file(target_path) link_name = self.make_path('foo', 'bar') self.create_symlink(link_name, target_path) self.os.utime(link_name, (1, 2)) st = self.os.stat(link_name) self.assertEqual(1, st.st_atime) self.assertEqual(2, st.st_mtime) self.os.utime(link_name, (3, 4)) st = self.os.stat(link_name) self.assertEqual(3, st.st_atime) self.assertEqual(4, st.st_mtime) def test_too_many_links(self): self.check_posix_only() link_path = self.make_path('a', 'loop') self.create_symlink(link_path, 'loop') self.assertFalse(self.os.path.exists(link_path)) def test_that_drive_letters_are_preserved(self): self.check_windows_only() self.skip_real_fs() self.assertEqual('C:!foo!bar', self.filesystem.resolve_path('C:!foo!!bar')) def test_that_unc_paths_are_preserved(self): self.check_windows_only() self.skip_real_fs() self.assertEqual('!!foo!bar!baz', self.filesystem.resolve_path('!!foo!bar!baz!!')) class RealResolvePathTest(ResolvePathTest): def use_real_fs(self): return True if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_os_test.py0000666000000000000000000071274414147521400017475 0ustar 00000000000000# -*- coding: utf-8 -*- # # Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for fake_filesystem.FakeOpen.""" import errno import os import stat import sys import unittest from pyfakefs.helpers import IN_DOCKER, IS_PYPY from pyfakefs import fake_filesystem from pyfakefs.fake_filesystem import FakeFileOpen, is_root from pyfakefs.extra_packages import ( use_scandir, use_scandir_package, use_builtin_scandir ) from pyfakefs.tests.test_utils import TestCase, RealFsTestCase class FakeOsModuleTestBase(RealFsTestCase): def createTestFile(self, path): self.create_file(path) self.assertTrue(self.os.path.exists(path)) st = self.os.stat(path) self.assertEqual(0o666, stat.S_IMODE(st.st_mode)) self.assertTrue(st.st_mode & stat.S_IFREG) self.assertFalse(st.st_mode & stat.S_IFDIR) def createTestDirectory(self, path): self.create_dir(path) self.assertTrue(self.os.path.exists(path)) st = self.os.stat(path) self.assertEqual(0o777, stat.S_IMODE(st.st_mode)) self.assertFalse(st.st_mode & stat.S_IFREG) self.assertTrue(st.st_mode & stat.S_IFDIR) class FakeOsModuleTest(FakeOsModuleTestBase): def setUp(self): super(FakeOsModuleTest, self).setUp() self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK self.rw = self.os.R_OK | self.os.W_OK def test_chdir(self): """chdir should work on a directory.""" directory = self.make_path('foo') self.create_dir(directory) self.os.chdir(directory) def test_chdir_fails_non_exist(self): """chdir should raise OSError if the target does not exist.""" directory = self.make_path('no', 'such', 'directory') self.assert_raises_os_error(errno.ENOENT, self.os.chdir, directory) def test_chdir_fails_non_directory(self): """chdir should raise OSError if the target is not a directory.""" filename = self.make_path('foo', 'bar') self.create_file(filename) self.assert_raises_os_error(errno.ENOTDIR, self.os.chdir, filename) def test_consecutive_chdir(self): """Consecutive relative chdir calls should work.""" dir1 = self.make_path('foo') dir2 = 'bar' full_dirname = self.os.path.join(dir1, dir2) self.create_dir(full_dirname) self.os.chdir(dir1) self.os.chdir(dir2) # use real path to handle symlink /var to /private/var in MacOs self.assertEqual(os.path.realpath(self.os.getcwd()), os.path.realpath(full_dirname)) def test_backwards_chdir(self): """chdir into '..' should behave appropriately.""" # skipping real fs test - can't test root dir self.skip_real_fs() rootdir = self.os.getcwd() dirname = 'foo' abs_dirname = self.os.path.abspath(dirname) self.filesystem.create_dir(dirname) self.os.chdir(dirname) self.assertEqual(abs_dirname, self.os.getcwd()) self.os.chdir('..') self.assertEqual(rootdir, self.os.getcwd()) self.os.chdir(self.os.path.join(dirname, '..')) self.assertEqual(rootdir, self.os.getcwd()) def test_get_cwd(self): # skipping real fs test - can't test root dir self.skip_real_fs() dirname = self.make_path('foo', 'bar') self.create_dir(dirname) self.assertEqual(self.os.getcwd(), self.os.path.sep) self.os.chdir(dirname) self.assertEqual(self.os.getcwd(), dirname) def test_listdir(self): self.assert_raises_os_error( errno.ENOENT, self.os.listdir, 'non_existing/fake_dir') directory = self.make_path('xyzzy', 'plugh') files = ['foo', 'bar', 'baz'] for f in files: self.create_file(self.os.path.join(directory, f)) files.sort() self.assertEqual(files, sorted(self.os.listdir(directory))) def test_listdir_uses_open_fd_as_path(self): self.check_posix_only() if os.listdir not in os.supports_fd: self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.listdir, 500) dir_path = self.make_path('xyzzy', 'plugh') files = ['foo', 'bar', 'baz'] for f in files: self.create_file(self.os.path.join(dir_path, f)) files.sort() path_des = self.os.open(dir_path, os.O_RDONLY) self.assertEqual(files, sorted(self.os.listdir(path_des))) def test_listdir_returns_list(self): directory_root = self.make_path('xyzzy') self.os.mkdir(directory_root) directory = self.os.path.join(directory_root, 'bug') self.os.mkdir(directory) self.create_file(self.make_path(directory, 'foo')) self.assertEqual(['foo'], self.os.listdir(directory)) def test_listdir_on_symlink(self): self.skip_if_symlink_not_supported() directory = self.make_path('xyzzy') files = ['foo', 'bar', 'baz'] for f in files: self.create_file(self.make_path(directory, f)) self.create_symlink(self.make_path('symlink'), self.make_path('xyzzy')) files.sort() self.assertEqual(files, sorted(self.os.listdir(self.make_path('symlink')))) def test_listdir_error(self): file_path = self.make_path('foo', 'bar', 'baz') self.create_file(file_path) self.assert_raises_os_error(errno.ENOTDIR, self.os.listdir, file_path) def test_exists_current_dir(self): self.assertTrue(self.os.path.exists('.')) def test_listdir_current(self): files = ['foo', 'bar', 'baz'] for f in files: self.create_file(self.make_path(f)) files.sort() self.assertEqual(files, sorted(self.os.listdir(self.base_path))) def test_fdopen(self): file_path1 = self.make_path('some_file1') self.create_file(file_path1, contents='contents here1') with self.open(file_path1, 'r') as fake_file1: fileno = fake_file1.fileno() fake_file2 = self.os.fdopen(fileno) self.assertNotEqual(fake_file2, fake_file1) with self.assertRaises(TypeError): self.os.fdopen(None) with self.assertRaises(TypeError): self.os.fdopen('a string') def test_out_of_range_fdopen(self): # test some file descriptor that is clearly out of range self.assert_raises_os_error(errno.EBADF, self.os.fdopen, 500) def test_closed_file_descriptor(self): first_path = self.make_path('some_file1') second_path = self.make_path('some_file2') third_path = self.make_path('some_file3') self.create_file(first_path, contents='contents here1') self.create_file(second_path, contents='contents here2') self.create_file(third_path, contents='contents here3') fake_file1 = self.open(first_path, 'r') fake_file2 = self.open(second_path, 'r') fake_file3 = self.open(third_path, 'r') fileno1 = fake_file1.fileno() fileno2 = fake_file2.fileno() fileno3 = fake_file3.fileno() self.os.close(fileno2) self.assert_raises_os_error(errno.EBADF, self.os.close, fileno2) self.assertEqual(fileno1, fake_file1.fileno()) self.assertEqual(fileno3, fake_file3.fileno()) with self.os.fdopen(fileno1) as f: self.assertFalse(f is fake_file1) with self.os.fdopen(fileno3) as f: self.assertFalse(f is fake_file3) self.assert_raises_os_error(errno.EBADF, self.os.fdopen, fileno2) def test_fdopen_mode(self): self.skip_real_fs() file_path1 = self.make_path('some_file1') self.create_file(file_path1, contents='contents here1') self.os.chmod(file_path1, (stat.S_IFREG | 0o666) ^ stat.S_IWRITE) fake_file1 = self.open(file_path1, 'r') fileno1 = fake_file1.fileno() self.os.fdopen(fileno1) self.os.fdopen(fileno1, 'r') if not is_root(): with self.assertRaises(OSError): self.os.fdopen(fileno1, 'w') else: self.os.fdopen(fileno1, 'w') self.os.close(fileno1) def test_fstat(self): directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path, contents='ABCDE') with self.open(file_path) as file_obj: fileno = file_obj.fileno() self.assertTrue(stat.S_IFREG & self.os.fstat(fileno)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.fstat(fileno).st_mode) self.assertEqual(5, self.os.fstat(fileno)[stat.ST_SIZE]) def test_stat(self): directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path, contents='ABCDE') self.assertTrue(stat.S_IFDIR & self.os.stat(directory)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.stat(file_path)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode) self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE]) def test_stat_with_unc_path(self): self.skip_real_fs() self.check_windows_only() directory = '//root/share/dir' file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path, contents='ABCDE') self.assertTrue(stat.S_IFDIR & self.os.stat(directory)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.stat(file_path)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode) self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE]) def test_stat_with_drive(self): self.skip_real_fs() self.check_windows_only() directory = 'C:/foo/dir' file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path, contents='ABCDE') self.assertTrue(stat.S_IFDIR & self.os.stat(directory)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.stat(file_path)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.stat(file_path).st_mode) self.assertEqual(5, self.os.stat(file_path)[stat.ST_SIZE]) def test_stat_uses_open_fd_as_path(self): self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.stat, 5) file_path = self.make_path('foo', 'bar') self.create_file(file_path) with self.open(file_path) as f: self.assertTrue( stat.S_IFREG & self.os.stat(f.filedes)[stat.ST_MODE]) def test_stat_no_follow_symlinks_posix(self): """Test that stat with follow_symlinks=False behaves like lstat.""" self.check_posix_only() directory = self.make_path('xyzzy') base_name = 'plugh' file_contents = 'frobozz' # Just make sure we didn't accidentally make our test data meaningless. self.assertNotEqual(len(base_name), len(file_contents)) file_path = self.os.path.join(directory, base_name) link_path = self.os.path.join(directory, 'link') self.create_file(file_path, contents=file_contents) self.create_symlink(link_path, base_name) self.assertEqual(len(file_contents), self.os.stat(file_path, follow_symlinks=False)[ stat.ST_SIZE]) self.assertEqual(len(base_name), self.os.stat(link_path, follow_symlinks=False)[ stat.ST_SIZE]) def test_stat_no_follow_symlinks_windows(self): """Test that stat with follow_symlinks=False behaves like lstat.""" self.check_windows_only() self.skip_if_symlink_not_supported() directory = self.make_path('xyzzy') base_name = 'plugh' file_contents = 'frobozz' # Just make sure we didn't accidentally make our test data meaningless. self.assertNotEqual(len(base_name), len(file_contents)) file_path = self.os.path.join(directory, base_name) link_path = self.os.path.join(directory, 'link') self.create_file(file_path, contents=file_contents) self.create_symlink(link_path, base_name) self.assertEqual(len(file_contents), self.os.stat(file_path, follow_symlinks=False)[ stat.ST_SIZE]) self.assertEqual(0, self.os.stat(link_path, follow_symlinks=False)[ stat.ST_SIZE]) def test_lstat_size_posix(self): self.check_posix_only() directory = self.make_path('xyzzy') base_name = 'plugh' file_contents = 'frobozz' # Just make sure we didn't accidentally make our test data meaningless. self.assertNotEqual(len(base_name), len(file_contents)) file_path = self.os.path.join(directory, base_name) link_path = self.os.path.join(directory, 'link') self.create_file(file_path, contents=file_contents) self.create_symlink(link_path, base_name) self.assertEqual(len(file_contents), self.os.lstat(file_path)[stat.ST_SIZE]) self.assertEqual(len(base_name), self.os.lstat(link_path)[stat.ST_SIZE]) def test_lstat_size_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() directory = self.make_path('xyzzy') base_name = 'plugh' file_contents = 'frobozz' # Just make sure we didn't accidentally make our test data meaningless. self.assertNotEqual(len(base_name), len(file_contents)) file_path = self.os.path.join(directory, base_name) link_path = self.os.path.join(directory, 'link') self.create_file(file_path, contents=file_contents) self.create_symlink(link_path, base_name) self.assertEqual(len(file_contents), self.os.lstat(file_path)[stat.ST_SIZE]) self.assertEqual(0, self.os.lstat(link_path)[stat.ST_SIZE]) def test_lstat_trailing_sep(self): # regression test for #342 stat_result = self.os.lstat(self.base_path) self.assertEqual(stat_result, self.os.lstat(self.base_path + self.path_separator())) self.assertEqual(stat_result, self.os.lstat( self.base_path + self.path_separator() + self.path_separator())) def test_stat_with_byte_string(self): stat_str = self.os.stat(self.base_path) base_path_bytes = self.base_path.encode('utf8') stat_bytes = self.os.stat(base_path_bytes) self.assertEqual(stat_bytes, stat_str) def test_lstat_with_byte_string(self): stat_str = self.os.lstat(self.base_path) base_path_bytes = self.base_path.encode('utf8') stat_bytes = self.os.lstat(base_path_bytes) self.assertEqual(stat_bytes, stat_str) def test_stat_with_current_dir(self): # regression test for #516 stat_result = self.os.stat('.') lstat_result = self.os.lstat('.') self.assertEqual(stat_result, lstat_result) def test_exists_with_trailing_sep(self): # regression test for #364 file_path = self.make_path('alpha') self.create_file(file_path) self.assertFalse(self.os.path.exists(file_path + self.os.sep)) def test_mkdir_with_trailing_sep(self): # regression test for #367 dir_path = self.make_path('foo') self.os.mkdir(dir_path + self.os.sep + self.os.sep) self.assertTrue(self.os.path.exists(dir_path)) def test_readlink_empty_path(self): self.check_posix_only() self.assert_raises_os_error(errno.ENOENT, self.os.readlink, '') def test_readlink_ending_with_sep_posix(self): # regression test for #359 self.check_posix_only() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) self.assert_raises_os_error(errno.EINVAL, self.os.readlink, link_path + self.os.sep) def test_lstat_symlink_with_trailing_sep_linux(self): # regression test for #366 self.check_linux_only() self.skip_if_symlink_not_supported() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) # used to raise self.assertTrue(self.os.lstat(link_path + self.os.sep).st_mode) def test_lstat_symlink_with_trailing_sep_macos(self): # regression test for #366 self.check_macos_only() self.skip_if_symlink_not_supported() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) # used to raise self.assertTrue(self.os.lstat(link_path + self.os.sep).st_mode) def test_readlink_ending_with_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) self.assert_equal_paths(self.base_path, self.os.readlink(link_path + self.os.sep)) def test_islink_with_trailing_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) self.assertTrue(self.os.path.islink(link_path + self.os.path.sep)) def test_islink_with_trailing_sep_linux(self): self.check_linux_only() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) self.assertFalse(self.os.path.islink(link_path + self.os.sep)) def test_islink_with_trailing_sep_macos(self): self.check_macos_only() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) self.assertFalse(self.os.path.islink(link_path + self.os.sep)) def check_getsize_raises_with_trailing_separator(self, error_nr): file_path = self.make_path('bar') self.create_file(file_path) self.assert_raises_os_error(error_nr, self.os.path.getsize, file_path + self.os.sep) def test_getsize_raises_with_trailing_separator_posix(self): self.check_posix_only() self.check_getsize_raises_with_trailing_separator(errno.ENOTDIR) def test_getsize_raises_with_trailing_separator_windows(self): self.check_windows_only() self.check_getsize_raises_with_trailing_separator(errno.EINVAL) def check_remove_link_ending_with_sep(self, error_nr): # regression test for #360 link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) self.assert_raises_os_error(error_nr, self.os.remove, link_path + self.os.sep) def test_remove_link_ending_with_sep_linux(self): self.check_linux_only() self.check_remove_link_ending_with_sep(errno.ENOTDIR) def test_remove_link_ending_with_sep_macos(self): self.check_macos_only() self.check_remove_link_ending_with_sep(errno.EPERM) def test_remove_link_ending_with_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() self.check_remove_link_ending_with_sep(errno.EACCES) def test_lstat_uses_open_fd_as_path(self): self.skip_if_symlink_not_supported() if os.lstat not in os.supports_fd: self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.lstat, 5) file_path = self.make_path('foo', 'bar') link_path = self.make_path('foo', 'link') file_contents = b'contents' self.create_file(file_path, contents=file_contents) self.create_symlink(link_path, file_path) with self.open(file_path) as f: self.assertEqual(len(file_contents), self.os.lstat(f.filedes)[stat.ST_SIZE]) def test_stat_non_existent_file(self): # set up file_path = self.make_path('non', 'existent', 'file') self.assertFalse(self.os.path.exists(file_path)) # actual tests try: # Use try-catch to check exception attributes. self.os.stat(file_path) self.fail('Exception is expected.') # COV_NF_LINE except OSError as os_error: self.assertEqual(errno.ENOENT, os_error.errno) self.assertEqual(file_path, os_error.filename) def check_open_raises_with_trailing_separator(self, error_nr): file_path = self.make_path('bar') + self.os.sep self.assert_raises_os_error(error_nr, self.os.open, file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) def test_open_raises_with_trailing_separator_linux(self): self.check_linux_only() self.check_open_raises_with_trailing_separator(errno.EISDIR) def test_open_raises_with_trailing_separator_macos(self): self.check_macos_only() self.check_open_raises_with_trailing_separator(errno.ENOENT) def test_open_raises_with_trailing_separator_windows(self): self.check_windows_only() self.check_open_raises_with_trailing_separator(errno.EINVAL) def test_lexists_with_trailing_separator_linux_windows(self): self.check_linux_and_windows() self.skip_if_symlink_not_supported() file_path = self.make_path('foo') self.os.symlink(file_path, file_path) self.assertFalse(self.os.path.lexists(file_path + self.os.sep)) def test_lexists_with_trailing_separator_macos(self): # regression test for #373 self.check_macos_only() file_path = self.make_path('foo') self.os.symlink(file_path, file_path) self.assertTrue(self.os.path.lexists(file_path + self.os.sep)) def test_islink_with_trailing_separator_linux_windows(self): self.check_linux_and_windows() self.skip_if_symlink_not_supported() file_path = self.make_path('foo') self.os.symlink(file_path, file_path) self.assertFalse(self.os.path.islink(file_path + self.os.sep)) def test_islink_with_trailing_separator_macos(self): # regression test for #373 self.check_macos_only() file_path = self.make_path('foo') self.os.symlink(file_path, file_path) self.assertTrue(self.os.path.islink(file_path + self.os.sep)) def test_isfile_with_trailing_separator_linux_windows(self): self.check_linux_and_windows() file_path = self.make_path('foo') self.create_file(file_path) self.assertFalse(self.os.path.isfile(file_path + self.os.sep)) def test_isfile_with_trailing_separator_macos(self): # regression test for #374 self.check_macos_only() file_path = self.make_path('foo') self.create_file(file_path) self.assertFalse(self.os.path.isfile(file_path + self.os.sep)) def test_isfile_not_readable_file(self): file_path = self.make_path('foo') self.create_file(file_path, perm=0) self.assertTrue(self.os.path.isfile(file_path)) def check_stat_with_trailing_separator(self, error_nr): # regression test for #376 file_path = self.make_path('foo') self.create_file(file_path) self.assert_raises_os_error(error_nr, self.os.stat, file_path + self.os.sep) def test_stat_with_trailing_separator_posix(self): self.check_posix_only() self.check_stat_with_trailing_separator(errno.ENOTDIR) def test_stat_with_trailing_separator_windows(self): self.check_windows_only() self.check_stat_with_trailing_separator(errno.EINVAL) def check_remove_with_trailing_separator(self, error_nr): # regression test for #377 file_path = self.make_path('foo') self.create_file(file_path) self.assert_raises_os_error(error_nr, self.os.remove, file_path + self.os.sep) def test_remove_with_trailing_separator_posix(self): self.check_posix_only() self.check_remove_with_trailing_separator(errno.ENOTDIR) def test_remove_with_trailing_separator_windows(self): self.check_windows_only() self.check_remove_with_trailing_separator(errno.EINVAL) def test_readlink(self): self.skip_if_symlink_not_supported() link_path = self.make_path('foo', 'bar', 'baz') target = self.make_path('tarJAY') self.create_symlink(link_path, target) self.assert_equal_paths(self.os.readlink(link_path), target) def check_readlink_raises_if_path_is_not_a_link(self): file_path = self.make_path('foo', 'bar', 'eleventyone') self.create_file(file_path) self.assert_raises_os_error(errno.EINVAL, self.os.readlink, file_path) def test_readlink_raises_if_path_is_not_a_link_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() self.check_readlink_raises_if_path_is_not_a_link() def test_readlink_raises_if_path_is_not_a_link_posix(self): self.check_posix_only() self.check_readlink_raises_if_path_is_not_a_link() def check_readlink_raises_if_path_has_file(self, error_subtype): self.create_file(self.make_path('a_file')) file_path = self.make_path('a_file', 'foo') self.assert_raises_os_error(error_subtype, self.os.readlink, file_path) file_path = self.make_path('a_file', 'foo', 'bar') self.assert_raises_os_error(error_subtype, self.os.readlink, file_path) def test_readlink_raises_if_path_has_file_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() self.check_readlink_raises_if_path_has_file(errno.ENOENT) def test_readlink_raises_if_path_has_file_posix(self): self.check_posix_only() self.check_readlink_raises_if_path_has_file(errno.ENOTDIR) def test_readlink_raises_if_path_does_not_exist(self): self.skip_if_symlink_not_supported() self.assert_raises_os_error(errno.ENOENT, self.os.readlink, '/this/path/does/not/exist') def test_readlink_raises_if_path_is_none(self): self.skip_if_symlink_not_supported() with self.assertRaises(TypeError): self.os.readlink(None) def test_broken_symlink_with_trailing_separator_linux(self): self.check_linux_only() file_path = self.make_path('foo') link_path = self.make_path('link') self.os.symlink(file_path, link_path) self.assert_raises_os_error(errno.EEXIST, self.os.symlink, link_path + self.os.sep, link_path + self.os.sep) def test_broken_symlink_with_trailing_separator_macos(self): # regression test for #371 self.check_macos_only() file_path = self.make_path('foo') link_path = self.make_path('link') self.os.symlink(file_path, link_path) self.os.symlink(link_path + self.os.sep, link_path + self.os.sep) def test_broken_symlink_with_trailing_separator_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() file_path = self.make_path('foo') link_path = self.make_path('link') self.os.symlink(file_path, link_path) self.assert_raises_os_error(errno.EINVAL, self.os.symlink, link_path + self.os.sep, link_path + self.os.sep) def test_circular_readlink_with_trailing_separator_linux(self): # Regression test for #372 self.check_linux_only() file_path = self.make_path('foo') self.os.symlink(file_path, file_path) self.assert_raises_os_error(errno.ELOOP, self.os.readlink, file_path + self.os.sep) def test_circular_readlink_with_trailing_separator_macos(self): # Regression test for #372 self.check_macos_only() file_path = self.make_path('foo') self.os.symlink(file_path, file_path) self.os.readlink(file_path + self.os.sep) def test_circular_readlink_with_trailing_separator_windows(self): # Regression test for #372 self.check_windows_only() self.skip_if_symlink_not_supported() file_path = self.make_path('foo') self.os.symlink(file_path, file_path) self.assert_raises_os_error(errno.EINVAL, self.os.readlink, file_path + self.os.sep) def test_readlink_with_links_in_path(self): self.skip_if_symlink_not_supported() self.create_symlink(self.make_path('meyer', 'lemon', 'pie'), self.make_path('yum')) self.create_symlink(self.make_path('geo', 'metro'), self.make_path('meyer')) self.assert_equal_paths(self.make_path('yum'), self.os.readlink( self.make_path('geo', 'metro', 'lemon', 'pie'))) def test_readlink_with_chained_links_in_path(self): self.skip_if_symlink_not_supported() self.create_symlink(self.make_path( 'eastern', 'european', 'wolfhounds', 'chase'), self.make_path('cats')) self.create_symlink(self.make_path('russian'), self.make_path('eastern', 'european')) self.create_symlink(self.make_path('dogs'), self.make_path('russian', 'wolfhounds')) self.assert_equal_paths(self.make_path('cats'), self.os.readlink( self.make_path('dogs', 'chase'))) def check_remove_dir(self, dir_error): directory = self.make_path('xyzzy') dir_path = self.os.path.join(directory, 'plugh') self.create_dir(dir_path) self.assertTrue(self.os.path.exists(dir_path)) self.assert_raises_os_error(dir_error, self.os.remove, dir_path) self.assertTrue(self.os.path.exists(dir_path)) self.os.chdir(directory) self.assert_raises_os_error(dir_error, self.os.remove, dir_path) self.assertTrue(self.os.path.exists(dir_path)) self.assert_raises_os_error(errno.ENOENT, self.os.remove, '/plugh') def test_remove_dir_linux(self): self.check_linux_only() self.check_remove_dir(errno.EISDIR) def test_remove_dir_mac_os(self): self.check_macos_only() self.check_remove_dir(errno.EPERM) def test_remove_dir_windows(self): self.check_windows_only() self.check_remove_dir(errno.EACCES) def test_remove_dir_with_drive(self): # regression test for issue #337 self.check_windows_only() self.skip_real_fs() dir_path = self.os.path.join('C:', 'test') self.filesystem.create_dir(dir_path) self.assert_raises_os_error(errno.EACCES, self.os.remove, dir_path) def test_remove_file(self): directory = self.make_path('zzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.os.remove(file_path) self.assertFalse(self.os.path.exists(file_path)) def test_remove_file_no_directory(self): directory = self.make_path('zzy') file_name = 'plugh' file_path = self.os.path.join(directory, file_name) self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.os.chdir(directory) self.os.remove(file_name) self.assertFalse(self.os.path.exists(file_path)) def test_remove_file_with_read_permission_raises_in_windows(self): self.check_windows_only() path = self.make_path('foo', 'bar') self.create_file(path) self.os.chmod(path, 0o444) self.assert_raises_os_error(errno.EACCES, self.os.remove, path) self.os.chmod(path, 0o666) def test_remove_file_with_read_permission_shall_succeed_in_posix(self): self.check_posix_only() path = self.make_path('foo', 'bar') self.create_file(path) self.os.chmod(path, 0o444) self.os.remove(path) self.assertFalse(self.os.path.exists(path)) def test_remove_file_without_parent_permission_raises_in_posix(self): self.check_posix_only() parent_dir = self.make_path('foo') path = self.os.path.join(parent_dir, 'bar') self.create_file(path) self.os.chmod(parent_dir, 0o666) # missing execute permission if not is_root(): self.assert_raises_os_error(errno.EACCES, self.os.remove, path) else: self.os.remove(path) self.assertFalse(self.os.path.exists(path)) self.create_file(path) self.os.chmod(parent_dir, 0o555) # missing write permission if not is_root(): self.assert_raises_os_error(errno.EACCES, self.os.remove, path) else: self.os.remove(path) self.assertFalse(self.os.path.exists(path)) self.create_file(path) self.os.chmod(parent_dir, 0o333) self.os.remove(path) self.assertFalse(self.os.path.exists(path)) def test_remove_open_file_fails_under_windows(self): self.check_windows_only() path = self.make_path('foo', 'bar') self.create_file(path) with self.open(path, 'r'): self.assert_raises_os_error(errno.EACCES, self.os.remove, path) self.assertTrue(self.os.path.exists(path)) def test_remove_open_file_possible_under_posix(self): self.check_posix_only() path = self.make_path('foo', 'bar') self.create_file(path) self.open(path, 'r') self.os.remove(path) self.assertFalse(self.os.path.exists(path)) def test_remove_file_relative_path(self): self.skip_real_fs() original_dir = self.os.getcwd() directory = self.make_path('zzy') subdirectory = self.os.path.join(directory, 'zzy') file_name = 'plugh' file_path = self.os.path.join(directory, file_name) file_path_relative = self.os.path.join('..', file_name) self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.create_dir(subdirectory) self.assertTrue(self.os.path.exists(subdirectory)) self.os.chdir(subdirectory) self.os.remove(file_path_relative) self.assertFalse(self.os.path.exists(file_path_relative)) self.os.chdir(original_dir) self.assertFalse(self.os.path.exists(file_path)) def check_remove_dir_raises_error(self, dir_error): directory = self.make_path('zzy') self.create_dir(directory) self.assert_raises_os_error(dir_error, self.os.remove, directory) def test_remove_dir_raises_error_linux(self): self.check_linux_only() self.check_remove_dir_raises_error(errno.EISDIR) def test_remove_dir_raises_error_mac_os(self): self.check_macos_only() self.check_remove_dir_raises_error(errno.EPERM) def test_remove_dir_raises_error_windows(self): self.check_windows_only() self.check_remove_dir_raises_error(errno.EACCES) def test_remove_symlink_to_dir(self): self.skip_if_symlink_not_supported() directory = self.make_path('zzy') link = self.make_path('link_to_dir') self.create_dir(directory) self.os.symlink(directory, link) self.assertTrue(self.os.path.exists(directory)) self.assertTrue(self.os.path.exists(link)) self.os.remove(link) self.assertTrue(self.os.path.exists(directory)) self.assertFalse(self.os.path.exists(link)) def test_unlink_raises_if_not_exist(self): file_path = self.make_path('file', 'does', 'not', 'exist') self.assertFalse(self.os.path.exists(file_path)) self.assert_raises_os_error(errno.ENOENT, self.os.unlink, file_path) def test_rename_to_nonexistent_file(self): """Can rename a file to an unused name.""" directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path, contents='test contents') self.assertTrue(self.os.path.exists(old_file_path)) self.assertFalse(self.os.path.exists(new_file_path)) self.os.rename(old_file_path, new_file_path) self.assertFalse(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.check_contents(new_file_path, 'test contents') def test_rename_dir_to_symlink_posix(self): self.check_posix_only() link_path = self.make_path('link') dir_path = self.make_path('dir') link_target = self.os.path.join(dir_path, 'link_target') self.create_dir(dir_path) self.os.symlink(link_target, link_path) self.assert_raises_os_error(errno.ENOTDIR, self.os.rename, dir_path, link_path) def test_rename_dir_to_symlink_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() link_path = self.make_path('link') dir_path = self.make_path('dir') link_target = self.os.path.join(dir_path, 'link_target') self.create_dir(dir_path) self.os.symlink(link_target, link_path) self.assert_raises_os_error(errno.EEXIST, self.os.rename, dir_path, link_path) def test_rename_file_to_symlink(self): self.check_posix_only() link_path = self.make_path('file_link') file_path = self.make_path('file') self.os.symlink(file_path, link_path) self.create_file(file_path) self.os.rename(file_path, link_path) self.assertFalse(self.os.path.exists(file_path)) self.assertTrue(self.os.path.exists(link_path)) self.assertTrue(self.os.path.isfile(link_path)) def test_rename_symlink_to_symlink(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') self.create_dir(base_path) link_path1 = self.os.path.join(base_path, 'link1') link_path2 = self.os.path.join(base_path, 'link2') self.os.symlink(base_path, link_path1) self.os.symlink(base_path, link_path2) self.os.rename(link_path1, link_path2) self.assertFalse(self.os.path.exists(link_path1)) self.assertTrue(self.os.path.exists(link_path2)) def test_rename_symlink_to_symlink_for_parent_raises(self): self.check_posix_only() dir_link = self.make_path('dir_link') dir_path = self.make_path('dir') dir_in_dir_path = self.os.path.join(dir_link, 'inner_dir') self.create_dir(dir_path) self.os.symlink(dir_path, dir_link) self.create_dir(dir_in_dir_path) self.assert_raises_os_error(errno.EINVAL, self.os.rename, dir_path, dir_in_dir_path) def check_rename_case_with_symlink(self, result): self.skip_if_symlink_not_supported() self.check_case_insensitive_fs() dir_path_lower = self.make_path('beta') self.create_dir(dir_path_lower) link_path = self.make_path('b') self.os.symlink(self.base_path, link_path) path1 = self.os.path.join(link_path, 'Beta') dir_path_upper = self.make_path('Beta') self.os.rename(path1, dir_path_upper) self.assertEqual(result, sorted(self.os.listdir(self.base_path))) def test_rename_case_with_symlink_mac(self): # Regression test for #322 self.check_macos_only() self.check_rename_case_with_symlink(['b', 'beta']) def test_rename_case_with_symlink_windows(self): self.check_windows_only() self.check_rename_case_with_symlink(['Beta', 'b']) def test_recursive_rename_raises(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') self.create_dir(base_path) new_path = self.os.path.join(base_path, 'new_dir') self.assert_raises_os_error(errno.EINVAL, self.os.rename, base_path, new_path) def test_rename_file_to_parent_dir_file(self): # Regression test for issue 230 dir_path = self.make_path('dir') self.create_dir(dir_path) file_path = self.make_path('old_file') new_file_path = self.os.path.join(dir_path, 'new_file') self.create_file(file_path) self.os.rename(file_path, new_file_path) def test_rename_with_target_parent_file_raises_posix(self): self.check_posix_only() file_path = self.make_path('foo', 'baz') self.create_file(file_path) self.assert_raises_os_error(errno.ENOTDIR, self.os.rename, file_path, file_path + '/new') def test_rename_with_target_parent_file_raises_windows(self): self.check_windows_only() file_path = self.make_path('foo', 'baz') self.create_file(file_path) self.assert_raises_os_error(errno.EACCES, self.os.rename, file_path, self.os.path.join(file_path, 'new')) def test_rename_symlink_to_source(self): self.check_posix_only() base_path = self.make_path('foo') link_path = self.os.path.join(base_path, 'slink') file_path = self.os.path.join(base_path, 'file') self.create_file(file_path) self.os.symlink(file_path, link_path) self.os.rename(link_path, file_path) self.assertFalse(self.os.path.exists(file_path)) def test_rename_symlink_to_dir_raises(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') link_path = self.os.path.join(base_path, 'dir_link') dir_path = self.os.path.join(base_path, 'dir') self.create_dir(dir_path) self.os.symlink(dir_path, link_path) self.assert_raises_os_error(errno.EISDIR, self.os.rename, link_path, dir_path) def test_rename_broken_symlink(self): self.check_posix_only() base_path = self.make_path('foo') self.create_dir(base_path) link_path = self.os.path.join(base_path, 'slink') file_path = self.os.path.join(base_path, 'file') self.os.symlink(file_path, link_path) self.os.rename(link_path, file_path) self.assertFalse(self.os.path.exists(file_path)) self.assertTrue(self.os.path.lexists(file_path)) self.assertFalse(self.os.path.exists(link_path)) def test_rename_directory(self): """Can rename a directory to an unused name.""" for old_path, new_path in [('wxyyw', 'xyzzy'), ('abccb', 'cdeed')]: old_path = self.make_path(old_path) new_path = self.make_path(new_path) self.create_file(self.os.path.join(old_path, 'plugh'), contents='test') self.assertTrue(self.os.path.exists(old_path)) self.assertFalse(self.os.path.exists(new_path)) self.os.rename(old_path, new_path) self.assertFalse(self.os.path.exists(old_path)) self.assertTrue(self.os.path.exists(new_path)) self.check_contents(self.os.path.join(new_path, 'plugh'), 'test') if not self.use_real_fs(): self.assertEqual(3, self.filesystem.get_object(new_path).st_nlink) def check_rename_directory_to_existing_file_raises(self, error_nr): dir_path = self.make_path('dir') file_path = self.make_path('file') self.create_dir(dir_path) self.create_file(file_path) self.assert_raises_os_error(error_nr, self.os.rename, dir_path, file_path) def test_rename_directory_to_existing_file_raises_posix(self): self.check_posix_only() self.check_rename_directory_to_existing_file_raises(errno.ENOTDIR) def test_rename_directory_to_existing_file_raises_windows(self): self.check_windows_only() self.check_rename_directory_to_existing_file_raises(errno.EEXIST) def test_rename_to_existing_directory_should_raise_under_windows(self): """Renaming to an existing directory raises OSError under Windows.""" self.check_windows_only() old_path = self.make_path('foo', 'bar') new_path = self.make_path('foo', 'baz') self.create_dir(old_path) self.create_dir(new_path) self.assert_raises_os_error(errno.EEXIST, self.os.rename, old_path, new_path) def test_rename_to_a_hardlink_of_same_file_should_do_nothing(self): self.skip_real_fs_failure(skip_posix=False) self.skip_if_symlink_not_supported() file_path = self.make_path('dir', 'file') self.create_file(file_path) link_path = self.make_path('link') self.os.link(file_path, link_path) self.os.rename(file_path, link_path) self.assertTrue(self.os.path.exists(file_path)) self.assertTrue(self.os.path.exists(link_path)) def test_hardlink_works_with_symlink(self): self.skip_if_symlink_not_supported() base_path = self.make_path('foo') self.create_dir(base_path) symlink_path = self.os.path.join(base_path, 'slink') self.os.symlink(base_path, symlink_path) file_path = self.os.path.join(base_path, 'slink', 'beta') self.create_file(file_path) link_path = self.os.path.join(base_path, 'slink', 'gamma') self.os.link(file_path, link_path) self.assertTrue(self.os.path.exists(link_path)) self.assertFalse(self.os.path.islink(link_path)) def test_replace_existing_directory_should_raise_under_windows(self): """Renaming to an existing directory raises OSError under Windows.""" self.check_windows_only() old_path = self.make_path('foo', 'bar') new_path = self.make_path('foo', 'baz') self.create_dir(old_path) self.create_dir(new_path) self.assert_raises_os_error(errno.EACCES, self.os.replace, old_path, new_path) def test_rename_to_existing_directory_under_posix(self): """Renaming to an existing directory changes the existing directory under Posix.""" self.check_posix_only() old_path = self.make_path('foo', 'bar') new_path = self.make_path('xyzzy') self.create_dir(self.os.path.join(old_path, 'sub')) self.create_dir(new_path) self.os.rename(old_path, new_path) self.assertTrue( self.os.path.exists(self.os.path.join(new_path, 'sub'))) self.assertFalse(self.os.path.exists(old_path)) def test_rename_file_to_existing_directory_raises_under_posix(self): self.check_posix_only() file_path = self.make_path('foo', 'bar', 'baz') new_path = self.make_path('xyzzy') self.create_file(file_path) self.create_dir(new_path) self.assert_raises_os_error(errno.EISDIR, self.os.rename, file_path, new_path) def test_rename_to_existing_dir_under_posix_raises_if_not_empty(self): """Renaming to an existing directory changes the existing directory under Posix.""" self.check_posix_only() old_path = self.make_path('foo', 'bar') new_path = self.make_path('foo', 'baz') self.create_dir(self.os.path.join(old_path, 'sub')) self.create_dir(self.os.path.join(new_path, 'sub')) # not testing specific subtype: # raises errno.ENOTEMPTY under Ubuntu 16.04, MacOS and pyfakefs # but raises errno.EEXIST at least under Ubunto 14.04 with self.assertRaises(OSError): self.os.rename(old_path, new_path) def test_rename_to_another_device_should_raise(self): """Renaming to another filesystem device raises OSError.""" self.skip_real_fs() self.filesystem.add_mount_point('/mount') old_path = '/foo/bar' new_path = '/mount/bar' self.filesystem.create_file(old_path) self.assert_raises_os_error(errno.EXDEV, self.os.rename, old_path, new_path) def test_rename_to_existent_file_posix(self): """Can rename a file to a used name under Unix.""" self.check_posix_only() directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path, contents='test contents 1') self.create_file(new_file_path, contents='test contents 2') self.assertTrue(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.os.rename(old_file_path, new_file_path) self.assertFalse(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.check_contents(new_file_path, 'test contents 1') def test_rename_to_existent_file_windows(self): """Renaming a file to a used name raises OSError under Windows.""" self.check_windows_only() directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path, contents='test contents 1') self.create_file(new_file_path, contents='test contents 2') self.assertTrue(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.assert_raises_os_error( errno.EEXIST, self.os.rename, old_file_path, new_file_path) def test_replace_to_existent_file(self): """Replaces an existing file (does not work with `rename()` under Windows).""" directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path, contents='test contents 1') self.create_file(new_file_path, contents='test contents 2') self.assertTrue(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.os.replace(old_file_path, new_file_path) self.assertFalse(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.check_contents(new_file_path, 'test contents 1') def test_rename_to_nonexistent_dir(self): """Can rename a file to a name in a nonexistent dir.""" directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join( directory, 'no_such_path', 'plugh_new') self.create_file(old_file_path, contents='test contents') self.assertTrue(self.os.path.exists(old_file_path)) self.assertFalse(self.os.path.exists(new_file_path)) self.assert_raises_os_error( errno.ENOENT, self.os.rename, old_file_path, new_file_path) self.assertTrue(self.os.path.exists(old_file_path)) self.assertFalse(self.os.path.exists(new_file_path)) self.check_contents(old_file_path, 'test contents') def test_rename_nonexistent_file_should_raise_error(self): """Can't rename a file that doesn't exist.""" self.assert_raises_os_error(errno.ENOENT, self.os.rename, 'nonexistent-foo', 'doesn\'t-matter-bar') def test_rename_empty_dir(self): """Test a rename of an empty directory.""" directory = self.make_path('xyzzy') before_dir = self.os.path.join(directory, 'empty') after_dir = self.os.path.join(directory, 'unused') self.create_dir(before_dir) self.assertTrue( self.os.path.exists(self.os.path.join(before_dir, '.'))) self.assertFalse(self.os.path.exists(after_dir)) self.os.rename(before_dir, after_dir) self.assertFalse(self.os.path.exists(before_dir)) self.assertTrue(self.os.path.exists(self.os.path.join(after_dir, '.'))) def test_rename_symlink(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') self.create_dir(base_path) link_path = self.os.path.join(base_path, 'link') self.os.symlink(base_path, link_path) file_path = self.os.path.join(link_path, 'file') new_file_path = self.os.path.join(link_path, 'new') self.create_file(file_path) self.os.rename(file_path, new_file_path) self.assertFalse(self.os.path.exists(file_path)) self.assertTrue(self.os.path.exists(new_file_path)) def check_append_mode_tell_after_truncate(self, tell_result): file_path = self.make_path('baz') with self.open(file_path, 'w') as f0: with self.open(file_path, 'a') as f1: f1.write('abcde') f0.seek(2) f0.truncate() self.assertEqual(tell_result, f1.tell()) with self.open(file_path, mode='rb') as f: self.assertEqual(b'\0\0abcde', f.read()) def test_append_mode_tell_linux_windows(self): # Regression test for #300 self.check_linux_and_windows() self.check_append_mode_tell_after_truncate(7) def test_append_mode_tell_macos(self): # Regression test for #300 self.check_macos_only() self.check_append_mode_tell_after_truncate(7) def test_tell_after_seek_in_append_mode(self): # Regression test for #363 file_path = self.make_path('foo') with self.open(file_path, 'a') as f: f.seek(1) self.assertEqual(1, f.tell()) def test_tell_after_seekback_in_append_mode(self): # Regression test for #414 file_path = self.make_path('foo') with self.open(file_path, 'a') as f: f.write('aa') f.seek(1) self.assertEqual(1, f.tell()) def test_dir_with_trailing_sep_is_dir(self): # regression test for #387 self.assertTrue(self, self.os.path.isdir(self.base_path + self.os.sep)) def check_rename_dir_with_trailing_sep(self, error): dir_path = self.make_path('dir') + self.os.sep self.os.mkdir(dir_path) self.assert_raises_os_error(error, self.os.rename, dir_path, self.base_path) def test_rename_dir_with_trailing_sep_posix(self): # regression test for #406 self.check_posix_only() self.check_rename_dir_with_trailing_sep(errno.ENOTEMPTY) def test_rename_dir_with_trailing_sep_windows(self): self.check_windows_only() self.check_rename_dir_with_trailing_sep(errno.EEXIST) def test_rename_dir(self): """Test a rename of a directory.""" directory = self.make_path('xyzzy') before_dir = self.os.path.join(directory, 'before') before_file = self.os.path.join(directory, 'before', 'file') after_dir = self.os.path.join(directory, 'after') after_file = self.os.path.join(directory, 'after', 'file') self.create_dir(before_dir) self.create_file(before_file, contents='payload') self.assertTrue(self.os.path.exists(before_dir)) self.assertTrue(self.os.path.exists(before_file)) self.assertFalse(self.os.path.exists(after_dir)) self.assertFalse(self.os.path.exists(after_file)) self.os.rename(before_dir, after_dir) self.assertFalse(self.os.path.exists(before_dir)) self.assertFalse(self.os.path.exists(before_file)) self.assertTrue(self.os.path.exists(after_dir)) self.assertTrue(self.os.path.exists(after_file)) self.check_contents(after_file, 'payload') def test_rename_preserves_stat(self): """Test if rename preserves mtime.""" self.check_posix_only() self.skip_real_fs() directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path) old_file = self.filesystem.get_object(old_file_path) old_file.st_mtime = old_file.st_mtime - 3600 self.os.chown(old_file_path, 200, 200) self.os.chmod(old_file_path, 0o222) self.create_file(new_file_path) new_file = self.filesystem.get_object(new_file_path) self.assertNotEqual(new_file.st_mtime, old_file.st_mtime) self.os.rename(old_file_path, new_file_path) new_file = self.filesystem.get_object( new_file_path, check_read_perm=False) self.assertEqual(new_file.st_mtime, old_file.st_mtime) self.assertEqual(new_file.st_mode, old_file.st_mode) self.assertEqual(new_file.st_uid, old_file.st_uid) self.assertEqual(new_file.st_gid, old_file.st_gid) def test_rename_same_filenames(self): """Test renaming when old and new names are the same.""" directory = self.make_path('xyzzy') file_contents = 'Spam eggs' file_path = self.os.path.join(directory, 'eggs') self.create_file(file_path, contents=file_contents) self.os.rename(file_path, file_path) self.check_contents(file_path, file_contents) def test_rmdir(self): """Can remove a directory.""" directory = self.make_path('xyzzy') sub_dir = self.make_path('xyzzy', 'abccd') other_dir = self.make_path('xyzzy', 'cdeed') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.os.rmdir(directory) self.assertFalse(self.os.path.exists(directory)) self.create_dir(sub_dir) self.create_dir(other_dir) self.os.chdir(sub_dir) self.os.rmdir('../cdeed') self.assertFalse(self.os.path.exists(other_dir)) self.os.chdir('..') self.os.rmdir('abccd') self.assertFalse(self.os.path.exists(sub_dir)) def test_rmdir_raises_if_not_empty(self): """Raises an exception if the target directory is not empty.""" directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(errno.ENOTEMPTY, self.os.rmdir, directory) def check_rmdir_raises_if_not_directory(self, error_nr): """Raises an exception if the target is not a directory.""" directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(errno.ENOTDIR, self.os.rmdir, file_path) self.assert_raises_os_error(error_nr, self.os.rmdir, '.') def test_rmdir_raises_if_not_directory_posix(self): self.check_posix_only() self.check_rmdir_raises_if_not_directory(errno.EINVAL) def test_rmdir_raises_if_not_directory_windows(self): self.check_windows_only() self.check_rmdir_raises_if_not_directory(errno.EACCES) def test_rmdir_raises_if_not_exist(self): """Raises an exception if the target does not exist.""" directory = self.make_path('xyzzy') self.assertFalse(self.os.path.exists(directory)) self.assert_raises_os_error(errno.ENOENT, self.os.rmdir, directory) def test_rmdir_via_symlink(self): self.check_windows_only() self.skip_if_symlink_not_supported() base_path = self.make_path('foo', 'bar') dir_path = self.os.path.join(base_path, 'alpha') self.create_dir(dir_path) link_path = self.os.path.join(base_path, 'beta') self.os.symlink(base_path, link_path) self.os.rmdir(link_path + '/alpha') self.assertFalse(self.os.path.exists(dir_path)) def remove_dirs_check(self, directory): self.assertTrue(self.os.path.exists(directory)) self.os.removedirs(directory) return not self.os.path.exists(directory) def test_removedirs(self): # no exception raised self.skip_real_fs() data = ['test1', ('test1', 'test2'), ('test1', 'extra'), ('test1', 'test2', 'test3')] for directory in data: self.create_dir(self.make_path(directory)) self.assertTrue(self.os.path.exists(self.make_path(directory))) self.assert_raises_os_error(errno.ENOTEMPTY, self.remove_dirs_check, self.make_path(data[0])) self.assert_raises_os_error(errno.ENOTEMPTY, self.remove_dirs_check, self.make_path(data[1])) self.assertTrue(self.remove_dirs_check(self.make_path(data[3]))) self.assertTrue(self.os.path.exists(self.make_path(data[0]))) self.assertFalse(self.os.path.exists(self.make_path(data[1]))) self.assertTrue(self.os.path.exists(self.make_path(data[2]))) # Should raise because '/test1/extra' is all that is left, and # removedirs('/test1/extra') will eventually try to rmdir('/'). self.assert_raises_os_error(errno.EBUSY, self.remove_dirs_check, self.make_path(data[2])) # However, it will still delete '/test1') in the process. self.assertFalse(self.os.path.exists(self.make_path(data[0]))) self.create_dir(self.make_path('test1', 'test2')) # Add this to the root directory to avoid raising an exception. self.filesystem.create_dir(self.make_path('test3')) self.assertTrue( self.remove_dirs_check(self.make_path('test1', 'test2'))) self.assertFalse(self.os.path.exists(self.make_path('test1', 'test2'))) self.assertFalse(self.os.path.exists(self.make_path('test1'))) def test_removedirs_raises_if_removing_root(self): """Raises exception if asked to remove '/'.""" self.skip_real_fs() self.os.rmdir(self.base_path) directory = self.os.path.splitdrive( self.base_path)[0] + self.os.path.sep self.assertTrue(self.os.path.exists(directory)) self.assert_raises_os_error(errno.EBUSY, self.os.removedirs, directory) def test_removedirs_raises_if_cascade_removing_root(self): """Raises exception if asked to remove '/' as part of a larger operation. All of other directories should still be removed, though. """ self.skip_real_fs() directory = self.make_path('foo', 'bar') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.assert_raises_os_error(errno.EBUSY, self.os.removedirs, directory) head, unused_tail = self.os.path.split(directory) while self.os.path.splitdrive(head)[1] != self.os.path.sep: self.assertFalse(self.os.path.exists(directory)) head, unused_tail = self.os.path.split(head) def test_removedirs_with_trailing_slash(self): """removedirs works on directory names with trailing slashes.""" # separate this case from the removing-root-directory case self.create_dir(self.make_path('baz')) directory = self.make_path('foo', 'bar') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.os.removedirs(directory) self.assertFalse(self.os.path.exists(directory)) def test_remove_dirs_with_top_symlink_fails(self): self.check_posix_only() dir_path = self.make_path('dir') dir_link = self.make_path('dir_link') self.create_dir(dir_path) self.os.symlink(dir_path, dir_link) self.assert_raises_os_error(errno.ENOTDIR, self.os.removedirs, dir_link) def test_remove_dirs_with_non_top_symlink_succeeds(self): self.check_posix_only() dir_path = self.make_path('dir') dir_link = self.make_path('dir_link') self.create_dir(dir_path) self.os.symlink(dir_path, dir_link) dir_in_dir = self.os.path.join(dir_link, 'dir2') self.create_dir(dir_in_dir) self.os.removedirs(dir_in_dir) self.assertFalse(self.os.path.exists(dir_in_dir)) # ensure that the symlink is not removed self.assertTrue(self.os.path.exists(dir_link)) def test_mkdir(self): """mkdir can create a relative directory.""" self.skip_real_fs() directory = 'xyzzy' self.assertFalse(self.filesystem.exists(directory)) self.os.mkdir(directory) self.assertTrue(self.filesystem.exists('/%s' % directory)) self.os.chdir(directory) self.os.mkdir(directory) self.assertTrue( self.filesystem.exists('/%s/%s' % (directory, directory))) self.os.chdir(directory) self.os.mkdir('../abccb') self.assertTrue(self.os.path.exists('/%s/abccb' % directory)) def test_mkdir_with_trailing_slash(self): """mkdir can create a directory named with a trailing slash.""" directory = self.make_path('foo') self.assertFalse(self.os.path.exists(directory)) self.os.mkdir(directory) self.assertTrue(self.os.path.exists(directory)) self.assertTrue(self.os.path.exists(self.make_path('foo'))) def test_mkdir_raises_if_empty_directory_name(self): """mkdir raises exeption if creating directory named ''.""" directory = '' self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory) def test_mkdir_raises_if_no_parent(self): """mkdir raises exception if parent directory does not exist.""" parent = 'xyzzy' directory = '%s/foo' % (parent,) self.assertFalse(self.os.path.exists(parent)) self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory) def test_mkdir_raises_on_symlink_in_posix(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') link_path = self.os.path.join(base_path, 'link_to_dir') dir_path = self.os.path.join(base_path, 'dir') self.create_dir(dir_path) self.os.symlink(dir_path, link_path) self.assert_raises_os_error(errno.ENOTDIR, self.os.rmdir, link_path) def test_mkdir_removes_symlink_in_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() base_path = self.make_path('foo', 'bar') link_path = self.os.path.join(base_path, 'link_to_dir') dir_path = self.os.path.join(base_path, 'dir') self.create_dir(dir_path) self.os.symlink(dir_path, link_path) self.os.rmdir(link_path) self.assertFalse(self.os.path.exists(link_path)) self.assertTrue(self.os.path.exists(dir_path)) def test_mkdir_raises_if_directory_exists(self): """mkdir raises exception if directory already exists.""" directory = self.make_path('xyzzy') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory) def test_mkdir_raises_if_file_exists(self): """mkdir raises exception if name already exists as a file.""" directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, file_path) def check_mkdir_raises_if_parent_is_file(self, error_type): """mkdir raises exception if name already exists as a file.""" directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assert_raises_os_error(error_type, self.os.mkdir, self.os.path.join(file_path, 'ff')) def test_mkdir_raises_if_parent_is_file_posix(self): self.check_posix_only() self.check_mkdir_raises_if_parent_is_file(errno.ENOTDIR) def test_mkdir_raises_if_parent_is_file_windows(self): self.check_windows_only() self.check_mkdir_raises_if_parent_is_file(errno.ENOENT) def test_mkdir_raises_with_slash_dot_posix(self): """mkdir raises exception if mkdir foo/. (trailing /.).""" self.check_posix_only() self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, self.os.sep + '.') directory = self.make_path('xyzzy', '.') self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory) self.create_dir(self.make_path('xyzzy')) self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory) def test_mkdir_raises_with_slash_dot_windows(self): """mkdir raises exception if mkdir foo/. (trailing /.).""" self.check_windows_only() self.assert_raises_os_error(errno.EACCES, self.os.mkdir, self.os.sep + '.') directory = self.make_path('xyzzy', '.') self.os.mkdir(directory) self.create_dir(self.make_path('xyzzy')) self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory) def test_mkdir_raises_with_double_dots_posix(self): """mkdir raises exception if mkdir foo/foo2/../foo3.""" self.check_posix_only() self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, self.os.sep + '..') directory = self.make_path('xyzzy', 'dir1', 'dir2', '..', '..', 'dir3') self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory) self.create_dir(self.make_path('xyzzy')) self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory) self.create_dir(self.make_path('xyzzy', 'dir1')) self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory) self.create_dir(self.make_path('xyzzy', 'dir1', 'dir2')) self.os.mkdir(directory) self.assertTrue(self.os.path.exists(directory)) directory = self.make_path('xyzzy', 'dir1', '..') self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory) def test_mkdir_raises_with_double_dots_windows(self): """mkdir raises exception if mkdir foo/foo2/../foo3.""" self.check_windows_only() self.assert_raises_os_error(errno.EACCES, self.os.mkdir, self.os.sep + '..') directory = self.make_path( 'xyzzy', 'dir1', 'dir2', '..', '..', 'dir3') self.assert_raises_os_error(errno.ENOENT, self.os.mkdir, directory) self.create_dir(self.make_path('xyzzy')) self.os.mkdir(directory) self.assertTrue(self.os.path.exists(directory)) directory = self.make_path('xyzzy', 'dir1', '..') self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory) def test_mkdir_raises_if_parent_is_read_only(self): """mkdir raises exception if parent is read only.""" self.check_posix_only() directory = self.make_path('a') self.os.mkdir(directory) # Change directory permissions to be read only. self.os.chmod(directory, 0o400) directory = self.make_path('a', 'b') if not is_root(): self.assert_raises_os_error(errno.EACCES, self.os.mkdir, directory) else: self.os.mkdir(directory) self.assertTrue(self.os.path.exists(directory)) def test_mkdir_with_with_symlink_parent(self): self.check_posix_only() dir_path = self.make_path('foo', 'bar') self.create_dir(dir_path) link_path = self.make_path('foo', 'link') self.os.symlink(dir_path, link_path) new_dir = self.os.path.join(link_path, 'new_dir') self.os.mkdir(new_dir) self.assertTrue(self.os.path.exists(new_dir)) def test_makedirs(self): """makedirs can create a directory even if parent does not exist.""" parent = self.make_path('xyzzy') directory = self.os.path.join(parent, 'foo') self.assertFalse(self.os.path.exists(parent)) self.os.makedirs(directory) self.assertTrue(self.os.path.exists(directory)) def check_makedirs_raises_if_parent_is_file(self, error_type): """makedirs raises exception if a parent component exists as a file.""" file_path = self.make_path('xyzzy') directory = self.os.path.join(file_path, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(error_type, self.os.makedirs, directory) def test_makedirs_raises_if_parent_is_file_posix(self): self.check_posix_only() self.check_makedirs_raises_if_parent_is_file(errno.ENOTDIR) def test_makedirs_raises_if_parent_is_file_windows(self): self.check_windows_only() self.check_makedirs_raises_if_parent_is_file(errno.ENOENT) def test_makedirs_raises_if_parent_is_broken_link(self): self.check_posix_only() link_path = self.make_path('broken_link') self.os.symlink(self.make_path('bogus'), link_path) self.assert_raises_os_error(errno.ENOENT, self.os.makedirs, self.os.path.join(link_path, 'newdir')) def test_makedirs_raises_if_parent_is_looping_link(self): self.skip_if_symlink_not_supported() link_path = self.make_path('link') link_target = self.os.path.join(link_path, 'link') self.os.symlink(link_target, link_path) self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, link_path) def test_makedirs_if_parent_is_symlink(self): self.check_posix_only() base_dir = self.make_path('foo', 'bar') self.create_dir(base_dir) link_dir = self.os.path.join(base_dir, 'linked') self.os.symlink(base_dir, link_dir) new_dir = self.os.path.join(link_dir, 'f') self.os.makedirs(name=new_dir) self.assertTrue(self.os.path.exists(new_dir)) def test_makedirs_raises_if_access_denied(self): """makedirs raises exception if access denied.""" self.check_posix_only() directory = self.make_path('a') self.os.mkdir(directory) # Change directory permissions to be read only. self.os.chmod(directory, 0o400) directory = self.make_path('a', 'b') if not is_root(): with self.assertRaises(Exception): self.os.makedirs(directory) else: self.os.makedirs(directory) self.assertTrue(self.os.path.exists(directory)) def test_makedirs_exist_ok(self): """makedirs uses the exist_ok argument""" directory = self.make_path('xyzzy', 'foo') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, directory) self.os.makedirs(directory, exist_ok=True) self.assertTrue(self.os.path.exists(directory)) def test_makedirs_in_write_protected_dir(self): self.check_posix_only() directory = self.make_path('foo') self.os.mkdir(directory, mode=0o555) subdir = self.os.path.join(directory, 'bar') if not is_root(): self.assert_raises_os_error(errno.EACCES, self.os.makedirs, subdir, exist_ok=True) self.assert_raises_os_error(errno.EACCES, self.os.makedirs, subdir, exist_ok=False) else: self.os.makedirs(subdir) self.assertTrue(self.os.path.exists(subdir)) def test_makedirs_raises_on_empty_path(self): self.assert_raises_os_error( errno.ENOENT, self.os.makedirs, '', exist_ok=False) self.assert_raises_os_error( errno.ENOENT, self.os.makedirs, '', exist_ok=True) # test fsync and fdatasync def test_fsync_raises_on_non_int(self): with self.assertRaises(TypeError): self.os.fsync("zero") def test_fdatasync_raises_on_non_int(self): self.check_linux_only() self.assertRaises(TypeError, self.os.fdatasync, "zero") def test_fsync_raises_on_invalid_fd(self): self.assert_raises_os_error(errno.EBADF, self.os.fsync, 500) def test_fdatasync_raises_on_invalid_fd(self): # No open files yet self.check_linux_only() self.assert_raises_os_error(errno.EINVAL, self.os.fdatasync, 0) self.assert_raises_os_error(errno.EBADF, self.os.fdatasync, 500) def test_fsync_pass_posix(self): self.check_posix_only() test_file_path = self.make_path('test_file') self.create_file(test_file_path, contents='dummy file contents') with self.open(test_file_path, 'r') as test_file: test_fd = test_file.fileno() # Test that this doesn't raise anything self.os.fsync(test_fd) # And just for sanity, double-check that this still raises self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd + 500) def test_fsync_pass_windows(self): self.check_windows_only() test_file_path = self.make_path('test_file') self.create_file(test_file_path, contents='dummy file contents') with self.open(test_file_path, 'r+') as test_file: test_fd = test_file.fileno() # Test that this doesn't raise anything self.os.fsync(test_fd) # And just for sanity, double-check that this still raises self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd + 500) with self.open(test_file_path, 'r') as test_file: test_fd = test_file.fileno() self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd) def test_fdatasync_pass(self): # setup self.check_linux_only() test_file_path = self.make_path('test_file') self.create_file(test_file_path, contents='dummy file contents') test_file = self.open(test_file_path, 'r') test_fd = test_file.fileno() # Test that this doesn't raise anything self.os.fdatasync(test_fd) # And just for sanity, double-check that this still raises self.assert_raises_os_error(errno.EBADF, self.os.fdatasync, test_fd + 500) def test_access700(self): # set up self.check_posix_only() path = self.make_path('some_file') self.createTestFile(path) self.os.chmod(path, 0o700) self.assert_mode_equal(0o700, self.os.stat(path).st_mode) # actual tests self.assertTrue(self.os.access(path, self.os.F_OK)) self.assertTrue(self.os.access(path, self.os.R_OK)) self.assertTrue(self.os.access(path, self.os.W_OK)) self.assertTrue(self.os.access(path, self.os.X_OK)) self.assertTrue(self.os.access(path, self.rwx)) def test_access600(self): # set up self.check_posix_only() path = self.make_path('some_file') self.createTestFile(path) self.os.chmod(path, 0o600) self.assert_mode_equal(0o600, self.os.stat(path).st_mode) # actual tests self.assertTrue(self.os.access(path, self.os.F_OK)) self.assertTrue(self.os.access(path, self.os.R_OK)) self.assertTrue(self.os.access(path, self.os.W_OK)) self.assertFalse(self.os.access(path, self.os.X_OK)) self.assertFalse(self.os.access(path, self.rwx)) self.assertTrue(self.os.access(path, self.rw)) def test_access400(self): # set up self.check_posix_only() path = self.make_path('some_file') self.createTestFile(path) self.os.chmod(path, 0o400) self.assert_mode_equal(0o400, self.os.stat(path).st_mode) # actual tests self.assertTrue(self.os.access(path, self.os.F_OK)) self.assertTrue(self.os.access(path, self.os.R_OK)) self.assertFalse(self.os.access(path, self.os.X_OK)) self.assertFalse(self.os.access(path, self.rwx)) if is_root(): self.assertTrue(self.os.access(path, self.os.W_OK)) self.assertTrue(self.os.access(path, self.rw)) else: self.assertFalse(self.os.access(path, self.os.W_OK)) self.assertFalse(self.os.access(path, self.rw)) def test_access_symlink(self): self.skip_if_symlink_not_supported() self.skip_real_fs() path = self.make_path('some_file') self.createTestFile(path) link_path = self.make_path('link_to_some_file') self.create_symlink(link_path, path) self.os.chmod(link_path, 0o400) # test file self.assertTrue(self.os.access(link_path, self.os.F_OK)) self.assertTrue(self.os.access(link_path, self.os.R_OK)) if is_root(): self.assertTrue(self.os.access(link_path, self.os.W_OK)) self.assertTrue(self.os.access(link_path, self.rw)) else: self.assertFalse(self.os.access(link_path, self.os.W_OK)) self.assertFalse(self.os.access(link_path, self.rw)) self.assertFalse(self.os.access(link_path, self.os.X_OK)) self.assertFalse(self.os.access(link_path, self.rwx)) # test link itself self.assertTrue( self.os.access(link_path, self.os.F_OK, follow_symlinks=False)) self.assertTrue( self.os.access(link_path, self.os.R_OK, follow_symlinks=False)) self.assertTrue( self.os.access(link_path, self.os.W_OK, follow_symlinks=False)) self.assertTrue( self.os.access(link_path, self.os.X_OK, follow_symlinks=False)) self.assertTrue( self.os.access(link_path, self.rwx, follow_symlinks=False)) self.assertTrue( self.os.access(link_path, self.rw, follow_symlinks=False)) def test_access_non_existent_file(self): # set up path = self.make_path('non', 'existent', 'file') self.assertFalse(self.os.path.exists(path)) # actual tests self.assertFalse(self.os.access(path, self.os.F_OK)) self.assertFalse(self.os.access(path, self.os.R_OK)) self.assertFalse(self.os.access(path, self.os.W_OK)) self.assertFalse(self.os.access(path, self.os.X_OK)) self.assertFalse(self.os.access(path, self.rwx)) self.assertFalse(self.os.access(path, self.rw)) def test_effective_ids_not_supported_under_windows(self): self.check_windows_only() path = self.make_path('foo', 'bar') with self.assertRaises(NotImplementedError): self.os.access(path, self.os.F_OK, effective_ids=True) def test_chmod(self): # set up self.check_posix_only() self.skip_real_fs() path = self.make_path('some_file') self.createTestFile(path) # actual tests self.os.chmod(path, 0o6543) st = self.os.stat(path) self.assert_mode_equal(0o6543, st.st_mode) self.assertTrue(st.st_mode & stat.S_IFREG) self.assertFalse(st.st_mode & stat.S_IFDIR) def test_chmod_uses_open_fd_as_path(self): self.check_posix_only() self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.chmod, 5, 0o6543) path = self.make_path('some_file') self.createTestFile(path) with self.open(path) as f: self.os.chmod(f.filedes, 0o6543) st = self.os.stat(path) self.assert_mode_equal(0o6543, st.st_mode) def test_chmod_follow_symlink(self): self.check_posix_only() path = self.make_path('some_file') self.createTestFile(path) link_path = self.make_path('link_to_some_file') self.create_symlink(link_path, path) self.os.chmod(link_path, 0o6543) st = self.os.stat(link_path) self.assert_mode_equal(0o6543, st.st_mode) st = self.os.stat(link_path, follow_symlinks=False) # the exact mode depends on OS and Python version self.assertEqual(stat.S_IMODE(0o700), stat.S_IMODE(st.st_mode) & 0o700) def test_chmod_no_follow_symlink(self): self.check_posix_only() path = self.make_path('some_file') self.createTestFile(path) link_path = self.make_path('link_to_some_file') self.create_symlink(link_path, path) if os.chmod not in os.supports_follow_symlinks or IS_PYPY: with self.assertRaises(NotImplementedError): self.os.chmod(link_path, 0o6543, follow_symlinks=False) else: self.os.chmod(link_path, 0o6543, follow_symlinks=False) st = self.os.stat(link_path) self.assert_mode_equal(0o666, st.st_mode) st = self.os.stat(link_path, follow_symlinks=False) self.assert_mode_equal(0o6543, st.st_mode) def test_lchmod(self): """lchmod shall behave like chmod with follow_symlinks=True.""" self.check_posix_only() self.skip_real_fs() path = self.make_path('some_file') self.createTestFile(path) link_path = self.make_path('link_to_some_file') self.create_symlink(link_path, path) self.os.lchmod(link_path, 0o6543) st = self.os.stat(link_path) self.assert_mode_equal(0o666, st.st_mode) st = self.os.lstat(link_path) self.assert_mode_equal(0o6543, st.st_mode) def test_chmod_dir(self): # set up self.check_posix_only() self.skip_real_fs() path = self.make_path('some_dir') self.createTestDirectory(path) # actual tests self.os.chmod(path, 0o1434) st = self.os.stat(path) self.assert_mode_equal(0o1434, st.st_mode) self.assertFalse(st.st_mode & stat.S_IFREG) self.assertTrue(st.st_mode & stat.S_IFDIR) def test_chmod_non_existent(self): # set up path = self.make_path('non', 'existent', 'file') self.assertFalse(self.os.path.exists(path)) # actual tests try: # Use try-catch to check exception attributes. self.os.chmod(path, 0o777) self.fail('Exception is expected.') # COV_NF_LINE except OSError as os_error: self.assertEqual(errno.ENOENT, os_error.errno) self.assertEqual(path, os_error.filename) def test_chown_existing_file(self): # set up self.skip_real_fs() file_path = self.make_path('some_file') self.create_file(file_path) # first set it make sure it's set self.os.chown(file_path, 100, 101) st = self.os.stat(file_path) self.assertEqual(st[stat.ST_UID], 100) self.assertEqual(st[stat.ST_GID], 101) # we can make sure it changed self.os.chown(file_path, 200, 201) st = self.os.stat(file_path) self.assertEqual(st[stat.ST_UID], 200) self.assertEqual(st[stat.ST_GID], 201) # setting a value to -1 leaves it unchanged self.os.chown(file_path, -1, -1) st = self.os.stat(file_path) self.assertEqual(st[stat.ST_UID], 200) self.assertEqual(st[stat.ST_GID], 201) def test_chown_uses_open_fd_as_path(self): self.check_posix_only() self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.chown, 5, 100, 101) file_path = self.make_path('foo', 'bar') self.create_file(file_path) with self.open(file_path) as f: self.os.chown(f.filedes, 100, 101) st = self.os.stat(file_path) self.assertEqual(st[stat.ST_UID], 100) def test_chown_follow_symlink(self): self.skip_real_fs() file_path = self.make_path('some_file') self.create_file(file_path) link_path = self.make_path('link_to_some_file') self.create_symlink(link_path, file_path) self.os.chown(link_path, 100, 101) st = self.os.stat(link_path) self.assertEqual(st[stat.ST_UID], 100) self.assertEqual(st[stat.ST_GID], 101) st = self.os.stat(link_path, follow_symlinks=False) self.assertNotEqual(st[stat.ST_UID], 100) self.assertNotEqual(st[stat.ST_GID], 101) def test_chown_no_follow_symlink(self): self.skip_real_fs() file_path = self.make_path('some_file') self.create_file(file_path) link_path = self.make_path('link_to_some_file') self.create_symlink(link_path, file_path) self.os.chown(link_path, 100, 101, follow_symlinks=False) st = self.os.stat(link_path) self.assertNotEqual(st[stat.ST_UID], 100) self.assertNotEqual(st[stat.ST_GID], 101) st = self.os.stat(link_path, follow_symlinks=False) self.assertEqual(st[stat.ST_UID], 100) self.assertEqual(st[stat.ST_GID], 101) def test_chown_bad_arguments(self): """os.chown() with bad args (Issue #30)""" self.check_posix_only() file_path = self.make_path('some_file') self.create_file(file_path) self.assertRaises(TypeError, self.os.chown, file_path, 'username', -1) self.assertRaises(TypeError, self.os.chown, file_path, -1, 'groupname') def test_chown_nonexisting_file_should_raise_os_error(self): self.check_posix_only() file_path = self.make_path('some_file') self.assertFalse(self.os.path.exists(file_path)) self.assert_raises_os_error( errno.ENOENT, self.os.chown, file_path, 100, 100) def test_classify_directory_contents(self): """Directory classification should work correctly.""" root_directory = self.make_path('foo') test_directories = ['bar1', 'baz2'] test_files = ['baz1', 'bar2', 'baz3'] self.create_dir(root_directory) for directory in test_directories: directory = self.os.path.join(root_directory, directory) self.create_dir(directory) for test_file in test_files: test_file = self.os.path.join(root_directory, test_file) self.create_file(test_file) test_directories.sort() test_files.sort() generator = self.os.walk(root_directory) root, dirs, files = next(generator) dirs.sort() files.sort() self.assertEqual(root_directory, root) self.assertEqual(test_directories, dirs) self.assertEqual(test_files, files) # os.mknod does not work under MacOS due to permission issues # so we test it under Linux only def test_mk_nod_can_create_a_file(self): self.check_linux_only() filename = self.make_path('foo') self.assertFalse(self.os.path.exists(filename)) self.os.mknod(filename) self.assertTrue(self.os.path.exists(filename)) self.assertEqual(stat.S_IFREG | 0o600, self.os.stat(filename).st_mode) def test_mk_nod_raises_if_empty_file_name(self): self.check_linux_only() filename = '' self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename) def test_mk_nod_raises_if_parent_dir_doesnt_exist(self): self.check_linux_only() parent = self.make_path('xyzzy') filename = self.os.path.join(parent, 'foo') self.assertFalse(self.os.path.exists(parent)) self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename) def test_mk_nod_raises_if_file_exists(self): self.check_linux_only() filename = self.make_path('tmp', 'foo') self.create_file(filename) self.assertTrue(self.os.path.exists(filename)) self.assert_raises_os_error(errno.EEXIST, self.os.mknod, filename) def test_mk_nod_raises_if_filename_is_dot(self): self.check_linux_only() filename = self.make_path('tmp', '.') self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename) def test_mk_nod_raises_if_filename_is_double_dot(self): self.check_linux_only() filename = self.make_path('tmp', '..') self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename) def test_mknod_empty_tail_for_existing_file_raises(self): self.check_linux_only() filename = self.make_path('foo') self.create_file(filename) self.assertTrue(self.os.path.exists(filename)) self.assert_raises_os_error(errno.EEXIST, self.os.mknod, filename) def test_mknod_empty_tail_for_nonexistent_file_raises(self): self.check_linux_only() filename = self.make_path('tmp', 'foo') self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename) def test_mknod_raises_if_filename_is_empty_string(self): self.check_linux_only() filename = '' self.assert_raises_os_error(errno.ENOENT, self.os.mknod, filename) def test_mknod_raises_if_unsupported_options(self): self.check_posix_only() # behavior seems to have changed in ubuntu-20.04, version 20210606.1 # skipping real fs tests for now self.skip_real_fs() filename = 'abcde' if not is_root(): self.assert_raises_os_error(errno.EPERM, self.os.mknod, filename, stat.S_IFCHR) else: self.os.mknod(filename, stat.S_IFCHR) self.os.remove(filename) def test_mknod_raises_if_parent_is_not_a_directory(self): self.check_linux_only() filename1 = self.make_path('foo') self.create_file(filename1) self.assertTrue(self.os.path.exists(filename1)) filename2 = self.make_path('foo', 'bar') self.assert_raises_os_error(errno.ENOTDIR, self.os.mknod, filename2) def test_symlink(self): self.skip_if_symlink_not_supported() file_path = self.make_path('foo', 'bar', 'baz') self.create_dir(self.make_path('foo', 'bar')) self.os.symlink('bogus', file_path) self.assertTrue(self.os.path.lexists(file_path)) self.assertFalse(self.os.path.exists(file_path)) self.create_file(self.make_path('foo', 'bar', 'bogus')) self.assertTrue(self.os.path.lexists(file_path)) self.assertTrue(self.os.path.exists(file_path)) def test_symlink_on_nonexisting_path_raises(self): self.check_posix_only() dir_path = self.make_path('bar') link_path = self.os.path.join(dir_path, 'bar') self.assert_raises_os_error(errno.ENOENT, self.os.symlink, link_path, link_path) self.assert_raises_os_error(errno.ENOENT, self.os.symlink, dir_path, link_path) def test_symlink_with_path_ending_with_sep_in_posix(self): self.check_posix_only() dir_path = self.make_path('dir') self.create_dir(dir_path) self.assert_raises_os_error(errno.EEXIST, self.os.symlink, self.base_path, dir_path + self.os.sep) dir_path = self.make_path('bar') self.assert_raises_os_error(errno.ENOENT, self.os.symlink, self.base_path, dir_path + self.os.sep) def test_symlink_with_path_ending_with_sep_in_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() dir_path = self.make_path('dir') self.create_dir(dir_path) self.assert_raises_os_error(errno.EEXIST, self.os.symlink, self.base_path, dir_path + self.os.sep) dir_path = self.make_path('bar') # does not raise under Windows self.os.symlink(self.base_path, dir_path + self.os.sep) def test_broken_symlink_with_trailing_sep_posix(self): # Regression test for #390 self.check_linux_only() path0 = self.make_path('foo') + self.os.sep self.assert_raises_os_error( errno.ENOENT, self.os.symlink, path0, path0) def test_broken_symlink_with_trailing_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() path0 = self.make_path('foo') + self.os.sep self.assert_raises_os_error( errno.EINVAL, self.os.symlink, path0, path0) def test_rename_symlink_with_trailing_sep_linux(self): # Regression test for #391 self.check_linux_only() path = self.make_path('foo') self.os.symlink(self.base_path, path) self.assert_raises_os_error(errno.ENOTDIR, self.os.rename, path + self.os.sep, self.base_path) def test_rename_symlink_with_trailing_sep_macos(self): # Regression test for #391 self.check_macos_only() path = self.make_path('foo') self.os.symlink(self.base_path, path) self.os.rename(path + self.os.sep, self.base_path) def test_rename_symlink_with_trailing_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() path = self.make_path('foo') self.os.symlink(self.base_path, path) self.assert_raises_os_error(errno.EEXIST, self.os.rename, path + self.os.sep, self.base_path) def test_rename_symlink_to_other_case(self): # Regression test for #389 self.skip_if_symlink_not_supported() link_path = self.make_path('foo') self.os.symlink(self.base_path, link_path) link_to_link_path = self.make_path('BAR') self.os.symlink(link_path, link_to_link_path) new_link_to_link_path = self.os.path.join(link_path, 'bar') self.os.rename(link_to_link_path, new_link_to_link_path) self.assertEqual(['bar', 'foo'], sorted(self.os.listdir(new_link_to_link_path))) def create_broken_link_path_with_trailing_sep(self): # Regression tests for #396 self.skip_if_symlink_not_supported() link_path = self.make_path('link') target_path = self.make_path('target') self.os.symlink(target_path, link_path) link_path += self.os.sep return link_path def test_lstat_broken_link_with_trailing_sep_linux(self): self.check_linux_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.ENOENT, self.os.lstat, link_path) def test_lstat_broken_link_with_trailing_sep_macos(self): self.check_macos_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.ENOENT, self.os.lstat, link_path) def test_lstat_broken_link_with_trailing_sep_windows(self): self.check_windows_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.EINVAL, self.os.lstat, link_path) def test_mkdir_broken_link_with_trailing_sep_linux_windows(self): self.check_linux_and_windows() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, link_path) self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, link_path) def test_mkdir_broken_link_with_trailing_sep_macos(self): self.check_macos_only() link_path = self.create_broken_link_path_with_trailing_sep() self.os.mkdir(link_path) # no error def test_makedirs_broken_link_with_trailing_sep_macos(self): self.check_macos_only() link_path = self.create_broken_link_path_with_trailing_sep() self.os.makedirs(link_path) # no error def test_remove_broken_link_with_trailing_sep_linux(self): self.check_linux_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.ENOTDIR, self.os.remove, link_path) def test_remove_broken_link_with_trailing_sep_macos(self): self.check_macos_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.ENOENT, self.os.remove, link_path) def test_remove_broken_link_with_trailing_sep_windows(self): self.check_windows_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.EINVAL, self.os.remove, link_path) def test_rename_broken_link_with_trailing_sep_linux(self): self.check_linux_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error( errno.ENOTDIR, self.os.rename, link_path, self.make_path('target')) def test_rename_broken_link_with_trailing_sep_macos(self): self.check_macos_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error( errno.ENOENT, self.os.rename, link_path, self.make_path('target')) def test_rename_broken_link_with_trailing_sep_windows(self): self.check_windows_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error( errno.EINVAL, self.os.rename, link_path, self.make_path('target')) def test_readlink_broken_link_with_trailing_sep_posix(self): self.check_posix_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.ENOENT, self.os.readlink, link_path) def test_readlink_broken_link_with_trailing_sep_windows(self): self.check_windows_only() link_path = self.create_broken_link_path_with_trailing_sep() self.assert_raises_os_error(errno.EINVAL, self.os.readlink, link_path) def test_islink_broken_link_with_trailing_sep(self): link_path = self.create_broken_link_path_with_trailing_sep() self.assertFalse(self.os.path.islink(link_path)) def test_lexists_broken_link_with_trailing_sep(self): link_path = self.create_broken_link_path_with_trailing_sep() self.assertFalse(self.os.path.lexists(link_path)) def test_rename_link_with_trailing_sep_to_self_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() path = self.make_path('foo') self.os.symlink(self.base_path, path) self.os.rename(path + self.os.sep, path) # no error def test_rename_link_with_trailing_sep_to_self_posix(self): # Regression test for #395 self.check_posix_only() path = self.make_path('foo') self.os.symlink(self.base_path, path) self.assert_raises_os_error( errno.ENOTDIR, self.os.rename, path + self.os.sep, path) def check_open_broken_symlink_to_path_with_trailing_sep(self, error): # Regression tests for #397 self.skip_if_symlink_not_supported() target_path = self.make_path('target') + self.os.sep link_path = self.make_path('link') self.os.symlink(target_path, link_path) self.assert_raises_os_error(error, self.open, link_path, 'a') self.assert_raises_os_error(error, self.open, link_path, 'w') def test_open_broken_symlink_to_path_with_trailing_sep_linux(self): self.check_linux_only() self.check_open_broken_symlink_to_path_with_trailing_sep(errno.EISDIR) def test_open_broken_symlink_to_path_with_trailing_sep_macos(self): self.check_macos_only() self.check_open_broken_symlink_to_path_with_trailing_sep(errno.ENOENT) def test_open_broken_symlink_to_path_with_trailing_sep_windows(self): self.check_windows_only() self.check_open_broken_symlink_to_path_with_trailing_sep(errno.EINVAL) def check_link_path_ending_with_sep(self, error): # Regression tests for #399 self.skip_if_symlink_not_supported() file_path = self.make_path('foo') link_path = self.make_path('link') with self.open(file_path, 'w'): self.assert_raises_os_error( error, self.os.link, file_path + self.os.sep, link_path) def test_link_path_ending_with_sep_posix(self): self.check_posix_only() self.check_link_path_ending_with_sep(errno.ENOTDIR) def test_link_path_ending_with_sep_windows(self): self.check_windows_only() self.check_link_path_ending_with_sep(errno.EINVAL) def test_link_to_path_ending_with_sep_posix(self): # regression test for #407 self.check_posix_only() path0 = self.make_path('foo') + self.os.sep path1 = self.make_path('bar') with self.open(path1, 'w'): self.assert_raises_os_error(errno.ENOENT, self.os.link, path1, path0) def test_link_to_path_ending_with_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() path0 = self.make_path('foo') + self.os.sep path1 = self.make_path('bar') with self.open(path1, 'w'): self.os.link(path1, path0) self.assertTrue(self.os.path.exists(path1)) def check_rename_to_path_ending_with_sep(self, error): # Regression tests for #400 file_path = self.make_path('foo') with self.open(file_path, 'w'): self.assert_raises_os_error( error, self.os.rename, file_path + self.os.sep, file_path) def test_rename_to_path_ending_with_sep_posix(self): self.check_posix_only() self.check_rename_to_path_ending_with_sep(errno.ENOTDIR) def test_rename_to_path_ending_with_sep_windows(self): self.check_windows_only() self.check_rename_to_path_ending_with_sep(errno.EINVAL) def test_rmdir_link_with_trailing_sep_linux(self): self.check_linux_only() dir_path = self.make_path('foo') self.os.mkdir(dir_path) link_path = self.make_path('link') self.os.symlink(dir_path, link_path) self.assert_raises_os_error( errno.ENOTDIR, self.os.rmdir, link_path + self.os.sep) def test_rmdir_link_with_trailing_sep_macos(self): # Regression test for #398 self.check_macos_only() dir_path = self.make_path('foo') self.os.mkdir(dir_path) link_path = self.make_path('link') self.os.symlink(dir_path, link_path) self.os.rmdir(link_path + self.os.sep) self.assertFalse(self.os.path.exists(link_path)) def test_rmdir_link_with_trailing_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() dir_path = self.make_path('foo') self.os.mkdir(dir_path) link_path = self.make_path('link') self.os.symlink(dir_path, link_path) self.os.rmdir(link_path + self.os.sep) self.assertFalse(self.os.path.exists(link_path)) def test_readlink_circular_link_with_trailing_sep_linux(self): self.check_linux_only() path1 = self.make_path('foo') path0 = self.make_path('bar') self.os.symlink(path0, path1) self.os.symlink(path1, path0) self.assert_raises_os_error( errno.ELOOP, self.os.readlink, path0 + self.os.sep) def test_readlink_circular_link_with_trailing_sep_macos(self): # Regression test for #392 self.check_macos_only() path1 = self.make_path('foo') path0 = self.make_path('bar') self.os.symlink(path0, path1) self.os.symlink(path1, path0) self.assertEqual(path0, self.os.readlink(path0 + self.os.sep)) def test_readlink_circular_link_with_trailing_sep_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() path1 = self.make_path('foo') path0 = self.make_path('bar') self.os.symlink(path0, path1) self.os.symlink(path1, path0) self.assert_raises_os_error( errno.EINVAL, self.os.readlink, path0 + self.os.sep) # hard link related tests def test_link_bogus(self): # trying to create a link from a non-existent file should fail self.skip_if_symlink_not_supported() self.assert_raises_os_error(errno.ENOENT, self.os.link, '/nonexistent_source', '/link_dest') def test_link_delete(self): self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') file2_path = self.make_path('test_file2') contents1 = 'abcdef' # Create file self.create_file(file1_path, contents=contents1) # link to second file self.os.link(file1_path, file2_path) # delete first file self.os.unlink(file1_path) # assert that second file exists, and its contents are the same self.assertTrue(self.os.path.exists(file2_path)) with self.open(file2_path) as f: self.assertEqual(f.read(), contents1) def test_link_update(self): self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') file2_path = self.make_path('test_file2') contents1 = 'abcdef' contents2 = 'ghijkl' # Create file and link self.create_file(file1_path, contents=contents1) self.os.link(file1_path, file2_path) # assert that the second file contains contents1 with self.open(file2_path) as f: self.assertEqual(f.read(), contents1) # update the first file with self.open(file1_path, 'w') as f: f.write(contents2) # assert that second file contains contents2 with self.open(file2_path) as f: self.assertEqual(f.read(), contents2) def test_link_non_existent_parent(self): self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') breaking_link_path = self.make_path('nonexistent', 'test_file2') contents1 = 'abcdef' # Create file and link self.create_file(file1_path, contents=contents1) # trying to create a link under a non-existent directory should fail self.assert_raises_os_error( errno.ENOENT, self.os.link, file1_path, breaking_link_path) def test_link_is_existing_file(self): self.skip_if_symlink_not_supported() file_path = self.make_path('foo', 'bar') self.create_file(file_path) self.assert_raises_os_error(errno.EEXIST, self.os.link, file_path, file_path) def test_link_target_is_dir_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() dir_path = self.make_path('foo', 'bar') link_path = self.os.path.join(dir_path, 'link') self.create_dir(dir_path) self.assert_raises_os_error(errno.EACCES, self.os.link, dir_path, link_path) def test_link_target_is_dir_posix(self): self.check_posix_only() dir_path = self.make_path('foo', 'bar') link_path = self.os.path.join(dir_path, 'link') self.create_dir(dir_path) self.assert_raises_os_error(errno.EPERM, self.os.link, dir_path, link_path) def test_link_count1(self): """Test that hard link counts are updated correctly.""" self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') file2_path = self.make_path('test_file2') file3_path = self.make_path('test_file3') self.create_file(file1_path) # initial link count should be one self.assertEqual(self.os.stat(file1_path).st_nlink, 1) self.os.link(file1_path, file2_path) # the count should be incremented for each hard link created self.assertEqual(self.os.stat(file1_path).st_nlink, 2) self.assertEqual(self.os.stat(file2_path).st_nlink, 2) # Check that the counts are all updated together self.os.link(file2_path, file3_path) self.assertEqual(self.os.stat(file1_path).st_nlink, 3) self.assertEqual(self.os.stat(file2_path).st_nlink, 3) self.assertEqual(self.os.stat(file3_path).st_nlink, 3) # Counts should be decremented when links are removed self.os.unlink(file3_path) self.assertEqual(self.os.stat(file1_path).st_nlink, 2) self.assertEqual(self.os.stat(file2_path).st_nlink, 2) # check that it gets decremented correctly again self.os.unlink(file1_path) self.assertEqual(self.os.stat(file2_path).st_nlink, 1) def test_nlink_for_directories(self): self.skip_real_fs() self.create_dir(self.make_path('foo', 'bar')) self.create_file(self.make_path('foo', 'baz')) self.assertEqual(2, self.filesystem.get_object( self.make_path('foo', 'bar')).st_nlink) self.assertEqual(4, self.filesystem.get_object( self.make_path('foo')).st_nlink) self.create_file(self.make_path('foo', 'baz2')) self.assertEqual(5, self.filesystem.get_object( self.make_path('foo')).st_nlink) def test_umask(self): self.check_posix_only() umask = os.umask(0o22) os.umask(umask) self.assertEqual(umask, self.os.umask(0o22)) def test_mkdir_umask_applied(self): """mkdir creates a directory with umask applied.""" self.check_posix_only() self.os.umask(0o22) dir1 = self.make_path('dir1') self.os.mkdir(dir1) self.assert_mode_equal(0o755, self.os.stat(dir1).st_mode) self.os.umask(0o67) dir2 = self.make_path('dir2') self.os.mkdir(dir2) self.assert_mode_equal(0o710, self.os.stat(dir2).st_mode) def test_makedirs_umask_applied(self): """makedirs creates a directories with umask applied.""" self.check_posix_only() self.os.umask(0o22) self.os.makedirs(self.make_path('p1', 'dir1')) self.assert_mode_equal( 0o755, self.os.stat(self.make_path('p1')).st_mode) self.assert_mode_equal( 0o755, self.os.stat(self.make_path('p1', 'dir1')).st_mode) self.os.umask(0o67) self.os.makedirs(self.make_path('p2', 'dir2')) self.assert_mode_equal( 0o710, self.os.stat(self.make_path('p2')).st_mode) self.assert_mode_equal( 0o710, self.os.stat(self.make_path('p2', 'dir2')).st_mode) def test_mknod_umask_applied(self): """mkdir creates a device with umask applied.""" # skipping MacOs due to mknod permission issues self.check_linux_only() self.os.umask(0o22) node1 = self.make_path('nod1') self.os.mknod(node1, stat.S_IFREG | 0o666) self.assert_mode_equal(0o644, self.os.stat(node1).st_mode) self.os.umask(0o27) node2 = self.make_path('nod2') self.os.mknod(node2, stat.S_IFREG | 0o666) self.assert_mode_equal(0o640, self.os.stat(node2).st_mode) def test_open_umask_applied(self): """open creates a file with umask applied.""" self.check_posix_only() self.os.umask(0o22) file1 = self.make_path('file1') self.open(file1, 'w').close() self.assert_mode_equal(0o644, self.os.stat(file1).st_mode) self.os.umask(0o27) file2 = self.make_path('file2') self.open(file2, 'w').close() self.assert_mode_equal(0o640, self.os.stat(file2).st_mode) def test_open_pipe(self): read_fd, write_fd = self.os.pipe() self.os.close(read_fd) self.os.close(write_fd) def test_open_pipe_with_existing_fd(self): file1 = self.make_path('file1') fd = self.os.open(file1, os.O_CREAT) read_fd, write_fd = self.os.pipe() self.assertGreater(read_fd, fd) self.os.close(fd) self.os.close(read_fd) self.os.close(write_fd) def test_open_file_with_existing_pipe(self): read_fd, write_fd = self.os.pipe() file1 = self.make_path('file1') fd = self.os.open(file1, os.O_CREAT) self.assertGreater(fd, write_fd) self.os.close(read_fd) self.os.close(write_fd) self.os.close(fd) def test_read_write_pipe(self): read_fd, write_fd = self.os.pipe() self.assertEqual(4, self.os.write(write_fd, b'test')) self.assertEqual(b'test', self.os.read(read_fd, 4)) self.os.close(read_fd) self.os.close(write_fd) def test_open_existing_pipe(self): # create some regular files to ensure that real and fake fd # are out of sync (see #581) fds = [] for i in range(5): path = self.make_path('file' + str(i)) fds.append(self.os.open(path, os.O_CREAT)) file_path = self.make_path('file.txt') self.create_file(file_path) with self.open(file_path): read_fd, write_fd = self.os.pipe() with self.open(write_fd, 'wb') as f: self.assertEqual(4, f.write(b'test')) with self.open(read_fd, 'rb') as f: self.assertEqual(b'test', f.read(4)) for fd in fds: self.os.close(fd) def test_write_to_pipe(self): read_fd, write_fd = self.os.pipe() self.os.write(write_fd, b'test') self.assertEqual(b'test', self.os.read(read_fd, 4)) self.os.close(read_fd) self.os.close(write_fd) def test_write_to_read_fd(self): read_fd, write_fd = self.os.pipe() self.assert_raises_os_error(errno.EBADF, self.os.write, read_fd, b'test') self.os.close(read_fd) self.os.close(write_fd) def test_truncate(self): file_path = self.make_path('foo', 'bar') self.create_file(file_path, contents='012345678901234567') self.os.truncate(file_path, 10) with self.open(file_path) as f: self.assertEqual('0123456789', f.read()) def test_truncate_non_existing(self): self.assert_raises_os_error(errno.ENOENT, self.os.truncate, 'foo', 10) def test_truncate_to_larger(self): file_path = self.make_path('foo', 'bar') self.create_file(file_path, contents='0123456789') fd = self.os.open(file_path, os.O_RDWR) self.os.truncate(fd, 20) self.assertEqual(20, self.os.stat(file_path).st_size) with self.open(file_path) as f: self.assertEqual('0123456789' + '\0' * 10, f.read()) def test_truncate_with_fd(self): if os.truncate not in os.supports_fd: self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.ftruncate, 50, 10) file_path = self.make_path('some_file') self.create_file(file_path, contents='01234567890123456789') fd = self.os.open(file_path, os.O_RDWR) self.os.truncate(fd, 10) self.assertEqual(10, self.os.stat(file_path).st_size) with self.open(file_path) as f: self.assertEqual('0123456789', f.read()) def test_ftruncate(self): if self.is_pypy: # not correctly supported self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.ftruncate, 50, 10) file_path = self.make_path('some_file') self.create_file(file_path, contents='0123456789012345') fd = self.os.open(file_path, os.O_RDWR) self.os.truncate(fd, 10) self.assertEqual(10, self.os.stat(file_path).st_size) with self.open(file_path) as f: self.assertEqual('0123456789', f.read()) class RealOsModuleTest(FakeOsModuleTest): def use_real_fs(self): return True class FakeOsModuleTestCaseInsensitiveFS(FakeOsModuleTestBase): def setUp(self): super(FakeOsModuleTestCaseInsensitiveFS, self).setUp() self.check_case_insensitive_fs() self.rwx = self.os.R_OK | self.os.W_OK | self.os.X_OK self.rw = self.os.R_OK | self.os.W_OK def test_chdir_fails_non_directory(self): """chdir should raise OSError if the target is not a directory.""" filename = self.make_path('foo', 'bar') self.create_file(filename) filename1 = self.make_path('Foo', 'Bar') self.assert_raises_os_error(errno.ENOTDIR, self.os.chdir, filename1) def test_listdir_returns_list(self): directory_root = self.make_path('xyzzy') self.os.mkdir(directory_root) directory = self.os.path.join(directory_root, 'bug') self.os.mkdir(directory) directory_upper = self.make_path('XYZZY', 'BUG') self.create_file(self.make_path(directory, 'foo')) self.assertEqual(['foo'], self.os.listdir(directory_upper)) def test_listdir_on_symlink(self): self.skip_if_symlink_not_supported() directory = self.make_path('xyzzy') files = ['foo', 'bar', 'baz'] for f in files: self.create_file(self.make_path(directory, f)) self.create_symlink(self.make_path('symlink'), self.make_path('xyzzy')) files.sort() self.assertEqual(files, sorted(self.os.listdir(self.make_path('SymLink')))) def test_fdopen_mode(self): self.skip_real_fs() file_path1 = self.make_path('some_file1') file_path2 = self.make_path('Some_File1') file_path3 = self.make_path('SOME_file1') self.create_file(file_path1, contents='contents here1') self.os.chmod(file_path2, (stat.S_IFREG | 0o666) ^ stat.S_IWRITE) fake_file1 = self.open(file_path3, 'r') fileno1 = fake_file1.fileno() self.os.fdopen(fileno1) self.os.fdopen(fileno1, 'r') if not is_root(): self.assertRaises(OSError, self.os.fdopen, fileno1, 'w') else: self.os.fdopen(fileno1, 'w') def test_stat(self): directory = self.make_path('xyzzy') directory1 = self.make_path('XYZZY') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path, contents='ABCDE') self.assertTrue(stat.S_IFDIR & self.os.stat(directory1)[stat.ST_MODE]) file_path1 = self.os.path.join(directory1, 'Plugh') self.assertTrue(stat.S_IFREG & self.os.stat(file_path1)[stat.ST_MODE]) self.assertTrue(stat.S_IFREG & self.os.stat(file_path1).st_mode) self.assertEqual(5, self.os.stat(file_path1)[stat.ST_SIZE]) def test_stat_no_follow_symlinks_posix(self): """Test that stat with follow_symlinks=False behaves like lstat.""" self.check_posix_only() directory = self.make_path('xyzzy') base_name = 'plugh' file_contents = 'frobozz' # Just make sure we didn't accidentally make our test data meaningless. self.assertNotEqual(len(base_name), len(file_contents)) file_path = self.os.path.join(directory, base_name) link_path = self.os.path.join(directory, 'link') self.create_file(file_path, contents=file_contents) self.create_symlink(link_path, base_name) self.assertEqual(len(file_contents), self.os.stat( file_path.upper(), follow_symlinks=False)[stat.ST_SIZE]) self.assertEqual(len(base_name), self.os.stat( link_path.upper(), follow_symlinks=False)[stat.ST_SIZE]) def test_lstat_posix(self): self.check_posix_only() directory = self.make_path('xyzzy') base_name = 'plugh' file_contents = 'frobozz' # Just make sure we didn't accidentally make our test data meaningless. self.assertNotEqual(len(base_name), len(file_contents)) file_path = self.os.path.join(directory, base_name) link_path = self.os.path.join(directory, 'link') self.create_file(file_path, contents=file_contents) self.create_symlink(link_path, base_name) self.assertEqual(len(file_contents), self.os.lstat(file_path.upper())[stat.ST_SIZE]) self.assertEqual(len(base_name), self.os.lstat(link_path.upper())[stat.ST_SIZE]) def test_readlink(self): self.skip_if_symlink_not_supported() link_path = self.make_path('foo', 'bar', 'baz') target = self.make_path('tarJAY') self.create_symlink(link_path, target) self.assert_equal_paths(self.os.readlink(link_path.upper()), target) def check_readlink_raises_if_path_not_a_link(self): file_path = self.make_path('foo', 'bar', 'eleventyone') self.create_file(file_path) self.assert_raises_os_error(errno.EINVAL, self.os.readlink, file_path.upper()) def test_readlink_raises_if_path_not_a_link_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() self.check_readlink_raises_if_path_not_a_link() def test_readlink_raises_if_path_not_a_link_posix(self): self.check_posix_only() self.check_readlink_raises_if_path_not_a_link() def check_readlink_raises_if_path_has_file(self, error_subtype): self.create_file(self.make_path('a_file')) file_path = self.make_path('a_file', 'foo') self.assert_raises_os_error(error_subtype, self.os.readlink, file_path.upper()) file_path = self.make_path('a_file', 'foo', 'bar') self.assert_raises_os_error(error_subtype, self.os.readlink, file_path.upper()) def test_readlink_raises_if_path_has_file_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() self.check_readlink_raises_if_path_has_file(errno.ENOENT) def test_readlink_raises_if_path_has_file_posix(self): self.check_posix_only() self.check_readlink_raises_if_path_has_file(errno.ENOTDIR) def test_readlink_with_links_in_path(self): self.skip_if_symlink_not_supported() self.create_symlink(self.make_path('meyer', 'lemon', 'pie'), self.make_path('yum')) self.create_symlink(self.make_path('geo', 'metro'), self.make_path('Meyer')) self.assert_equal_paths(self.make_path('yum'), self.os.readlink( self.make_path('Geo', 'Metro', 'Lemon', 'Pie'))) def test_readlink_with_chained_links_in_path(self): self.skip_if_symlink_not_supported() self.create_symlink(self.make_path( 'eastern', 'european', 'wolfhounds', 'chase'), self.make_path('cats')) self.create_symlink(self.make_path('russian'), self.make_path('Eastern', 'European')) self.create_symlink(self.make_path('dogs'), self.make_path('Russian', 'Wolfhounds')) self.assert_equal_paths(self.make_path('cats'), self.os.readlink( self.make_path('DOGS', 'Chase'))) def check_remove_dir(self, dir_error): directory = self.make_path('xyzzy') dir_path = self.os.path.join(directory, 'plugh') self.create_dir(dir_path) dir_path = dir_path.upper() self.assertTrue(self.os.path.exists(dir_path.upper())) self.assert_raises_os_error(dir_error, self.os.remove, dir_path) self.assertTrue(self.os.path.exists(dir_path)) self.os.chdir(directory) self.assert_raises_os_error(dir_error, self.os.remove, dir_path) self.assertTrue(self.os.path.exists(dir_path)) self.assert_raises_os_error(errno.ENOENT, self.os.remove, '/Plugh') def test_remove_dir_mac_os(self): self.check_macos_only() self.check_remove_dir(errno.EPERM) def test_remove_dir_windows(self): self.check_windows_only() self.check_remove_dir(errno.EACCES) def test_remove_file(self): directory = self.make_path('zzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path.upper())) self.os.remove(file_path.upper()) self.assertFalse(self.os.path.exists(file_path)) def test_remove_file_no_directory(self): directory = self.make_path('zzy') file_name = 'plugh' file_path = self.os.path.join(directory, file_name) self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.os.chdir(directory.upper()) self.os.remove(file_name.upper()) self.assertFalse(self.os.path.exists(file_path)) def test_remove_open_file_fails_under_windows(self): self.check_windows_only() path = self.make_path('foo', 'bar') self.create_file(path) with self.open(path, 'r'): self.assert_raises_os_error(errno.EACCES, self.os.remove, path.upper()) self.assertTrue(self.os.path.exists(path)) def test_remove_open_file_possible_under_posix(self): self.check_posix_only() path = self.make_path('foo', 'bar') self.create_file(path) self.open(path, 'r') self.os.remove(path.upper()) self.assertFalse(self.os.path.exists(path)) def test_remove_file_relative_path(self): self.skip_real_fs() original_dir = self.os.getcwd() directory = self.make_path('zzy') subdirectory = self.os.path.join(directory, 'zzy') file_name = 'plugh' file_path = self.os.path.join(directory, file_name) file_path_relative = self.os.path.join('..', file_name) self.create_file(file_path.upper()) self.assertTrue(self.os.path.exists(file_path)) self.create_dir(subdirectory) self.assertTrue(self.os.path.exists(subdirectory)) self.os.chdir(subdirectory.upper()) self.os.remove(file_path_relative.upper()) self.assertFalse(self.os.path.exists(file_path_relative)) self.os.chdir(original_dir.upper()) self.assertFalse(self.os.path.exists(file_path)) def check_remove_dir_raises_error(self, dir_error): directory = self.make_path('zzy') self.create_dir(directory) self.assert_raises_os_error(dir_error, self.os.remove, directory.upper()) def test_remove_dir_raises_error_mac_os(self): self.check_macos_only() self.check_remove_dir_raises_error(errno.EPERM) def test_remove_dir_raises_error_windows(self): self.check_windows_only() self.check_remove_dir_raises_error(errno.EACCES) def test_remove_symlink_to_dir(self): self.skip_if_symlink_not_supported() directory = self.make_path('zzy') link = self.make_path('link_to_dir') self.create_dir(directory) self.os.symlink(directory, link) self.assertTrue(self.os.path.exists(directory)) self.assertTrue(self.os.path.exists(link)) self.os.remove(link.upper()) self.assertTrue(self.os.path.exists(directory)) self.assertFalse(self.os.path.exists(link)) def test_rename_dir_to_symlink_posix(self): self.check_posix_only() link_path = self.make_path('link') dir_path = self.make_path('dir') link_target = self.os.path.join(dir_path, 'link_target') self.create_dir(dir_path) self.os.symlink(link_target.upper(), link_path.upper()) self.assert_raises_os_error(errno.ENOTDIR, self.os.rename, dir_path, link_path) def test_rename_dir_to_symlink_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() link_path = self.make_path('link') dir_path = self.make_path('dir') link_target = self.os.path.join(dir_path, 'link_target') self.create_dir(dir_path) self.os.symlink(link_target.upper(), link_path.upper()) self.assert_raises_os_error(errno.EEXIST, self.os.rename, dir_path, link_path) def test_rename_dir_to_existing_dir(self): # Regression test for #317 self.check_posix_only() dest_dir_path = self.make_path('Dest') # seems to behave differently under different MacOS versions self.skip_real_fs() new_dest_dir_path = self.make_path('dest') self.os.mkdir(dest_dir_path) source_dir_path = self.make_path('src') self.os.mkdir(source_dir_path) self.os.rename(source_dir_path, new_dest_dir_path) self.assertEqual(['dest'], self.os.listdir(self.base_path)) def test_rename_file_to_symlink(self): self.check_posix_only() link_path = self.make_path('file_link') file_path = self.make_path('file') self.os.symlink(file_path, link_path) self.create_file(file_path) self.os.rename(file_path.upper(), link_path) self.assertFalse(self.os.path.exists(file_path)) self.assertTrue(self.os.path.exists(link_path.upper())) self.assertTrue(self.os.path.isfile(link_path.upper())) def test_rename_symlink_to_symlink(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') self.create_dir(base_path) link_path1 = self.os.path.join(base_path, 'link1') link_path2 = self.os.path.join(base_path, 'link2') self.os.symlink(base_path.upper(), link_path1) self.os.symlink(base_path, link_path2) self.os.rename(link_path1.upper(), link_path2.upper()) self.assertFalse(self.os.path.exists(link_path1)) self.assertTrue(self.os.path.exists(link_path2)) def test_rename_symlink_to_symlink_for_parent_raises(self): self.check_posix_only() dir_link = self.make_path('dir_link') dir_path = self.make_path('dir') dir_in_dir_path = self.os.path.join(dir_link, 'inner_dir') self.create_dir(dir_path) self.os.symlink(dir_path.upper(), dir_link) self.create_dir(dir_in_dir_path) self.assert_raises_os_error(errno.EINVAL, self.os.rename, dir_path, dir_in_dir_path.upper()) def test_rename_directory_to_linked_dir(self): # Regression test for #314 self.skip_if_symlink_not_supported() link_path = self.make_path('link') self.os.symlink(self.base_path, link_path) link_subdir = self.os.path.join(link_path, 'dir') dir_path = self.make_path('Dir') self.os.mkdir(dir_path) self.os.rename(dir_path, link_subdir) self.assertEqual(['dir', 'link'], sorted(self.os.listdir(self.base_path))) def test_recursive_rename_raises(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') self.create_dir(base_path) new_path = self.os.path.join(base_path, 'new_dir') self.assert_raises_os_error(errno.EINVAL, self.os.rename, base_path.upper(), new_path) def test_rename_with_target_parent_file_raises_posix(self): self.check_posix_only() file_path = self.make_path('foo', 'baz') self.create_file(file_path) self.assert_raises_os_error(errno.ENOTDIR, self.os.rename, file_path, file_path.upper() + '/new') def test_rename_with_target_parent_file_raises_windows(self): self.check_windows_only() file_path = self.make_path('foo', 'baz') self.create_file(file_path) self.assert_raises_os_error( errno.EACCES, self.os.rename, file_path, self.os.path.join(file_path.upper(), 'new')) def test_rename_looping_symlink(self): # Regression test for #315 self.skip_if_symlink_not_supported() path_lower = self.make_path('baz') path_upper = self.make_path('BAZ') self.os.symlink(path_lower, path_upper) self.os.rename(path_upper, path_lower) self.assertEqual(['baz'], self.os.listdir(self.base_path)) def test_rename_symlink_to_source(self): self.check_posix_only() base_path = self.make_path('foo') link_path = self.os.path.join(base_path, 'slink') file_path = self.os.path.join(base_path, 'file') self.create_file(file_path) self.os.symlink(file_path, link_path) self.os.rename(link_path.upper(), file_path.upper()) self.assertFalse(self.os.path.exists(file_path)) def test_rename_symlink_to_dir_raises(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') link_path = self.os.path.join(base_path, 'dir_link') dir_path = self.os.path.join(base_path, 'dir') self.create_dir(dir_path) self.os.symlink(dir_path, link_path.upper()) self.assert_raises_os_error(errno.EISDIR, self.os.rename, link_path, dir_path.upper()) def test_rename_broken_symlink(self): self.check_posix_only() base_path = self.make_path('foo') self.create_dir(base_path) link_path = self.os.path.join(base_path, 'slink') file_path = self.os.path.join(base_path, 'file') self.os.symlink(file_path.upper(), link_path) self.os.rename(link_path.upper(), file_path) self.assertFalse(self.os.path.exists(file_path)) self.assertTrue(self.os.path.lexists(file_path)) self.assertFalse(self.os.path.exists(link_path)) def test_change_case_in_case_insensitive_file_system(self): """Can use `rename()` to change filename case in a case-insensitive file system.""" old_file_path = self.make_path('fileName') new_file_path = self.make_path('FileNAME') self.create_file(old_file_path, contents='test contents') self.assertEqual(['fileName'], self.os.listdir(self.base_path)) self.os.rename(old_file_path, new_file_path) self.assertTrue(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.assertEqual(['FileNAME'], self.os.listdir(self.base_path)) def test_rename_symlink_with_changed_case(self): # Regression test for #313 self.skip_if_symlink_not_supported() link_path = self.make_path('link') self.os.symlink(self.base_path, link_path) link_path = self.os.path.join(link_path, 'link') link_path_upper = self.make_path('link', 'LINK') self.os.rename(link_path_upper, link_path) def test_rename_directory(self): """Can rename a directory to an unused name.""" for old_path, new_path in [('wxyyw', 'xyzzy'), ('abccb', 'cdeed')]: old_path = self.make_path(old_path) new_path = self.make_path(new_path) self.create_file(self.os.path.join(old_path, 'plugh'), contents='test') self.assertTrue(self.os.path.exists(old_path)) self.assertFalse(self.os.path.exists(new_path)) self.os.rename(old_path.upper(), new_path.upper()) self.assertFalse(self.os.path.exists(old_path)) self.assertTrue(self.os.path.exists(new_path)) self.check_contents(self.os.path.join(new_path, 'plugh'), 'test') if not self.use_real_fs(): self.assertEqual(3, self.filesystem.get_object(new_path).st_nlink) def check_rename_directory_to_existing_file_raises(self, error_nr): dir_path = self.make_path('dir') file_path = self.make_path('file') self.create_dir(dir_path) self.create_file(file_path) self.assert_raises_os_error(error_nr, self.os.rename, dir_path, file_path.upper()) def test_rename_directory_to_existing_file_raises_posix(self): self.check_posix_only() self.check_rename_directory_to_existing_file_raises(errno.ENOTDIR) def test_rename_directory_to_existing_file_raises_windows(self): self.check_windows_only() self.check_rename_directory_to_existing_file_raises(errno.EEXIST) def test_rename_to_existing_directory_should_raise_under_windows(self): """Renaming to an existing directory raises OSError under Windows.""" self.check_windows_only() old_path = self.make_path('foo', 'bar') new_path = self.make_path('foo', 'baz') self.create_dir(old_path) self.create_dir(new_path) self.assert_raises_os_error(errno.EEXIST, self.os.rename, old_path.upper(), new_path.upper()) def test_rename_to_a_hardlink_of_same_file_should_do_nothing(self): self.skip_real_fs_failure(skip_posix=False) self.skip_if_symlink_not_supported() file_path = self.make_path('dir', 'file') self.create_file(file_path) link_path = self.make_path('link') self.os.link(file_path.upper(), link_path) self.os.rename(file_path, link_path.upper()) self.assertTrue(self.os.path.exists(file_path)) self.assertTrue(self.os.path.exists(link_path)) def test_rename_with_incorrect_source_case(self): # Regression test for #308 base_path = self.make_path('foo') path0 = self.os.path.join(base_path, 'bar') path1 = self.os.path.join(base_path, 'Bar') self.create_dir(path0) self.os.rename(path1, path0) self.assertTrue(self.os.path.exists(path0)) def test_rename_symlink_to_other_case_does_nothing_in_mac_os(self): # Regression test for #318 self.check_macos_only() path0 = self.make_path("beta") self.os.symlink(self.base_path, path0) path0 = self.make_path("beta", "Beta") path1 = self.make_path("Beta") self.os.rename(path0, path1) self.assertEqual(['beta'], sorted(self.os.listdir(path0))) def test_rename_symlink_to_other_case_works_in_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() path0 = self.make_path("beta") self.os.symlink(self.base_path, path0) path0 = self.make_path("beta", "Beta") path1 = self.make_path("Beta") self.os.rename(path0, path1) self.assertEqual(['Beta'], sorted(self.os.listdir(path0))) def test_stat_with_mixed_case(self): # Regression test for #310 self.skip_if_symlink_not_supported() base_path = self.make_path('foo') path = self.os.path.join(base_path, 'bar') self.create_dir(path) path = self.os.path.join(path, 'Bar') self.os.symlink(base_path, path) path = self.os.path.join(path, 'Bar') # used to raise self.os.stat(path) def test_hardlink_works_with_symlink(self): self.skip_if_symlink_not_supported() base_path = self.make_path('foo') self.create_dir(base_path) symlink_path = self.os.path.join(base_path, 'slink') self.os.symlink(base_path.upper(), symlink_path) file_path = self.os.path.join(base_path, 'slink', 'beta') self.create_file(file_path) link_path = self.os.path.join(base_path, 'Slink', 'gamma') self.os.link(file_path, link_path) self.assertTrue(self.os.path.exists(link_path)) def test_replace_existing_directory_should_raise_under_windows(self): """Renaming to an existing directory raises OSError under Windows.""" self.check_windows_only() old_path = self.make_path('foo', 'bar') new_path = self.make_path('foo', 'baz') self.create_dir(old_path) self.create_dir(new_path) self.assert_raises_os_error(errno.EACCES, self.os.replace, old_path, new_path.upper()) def test_rename_to_existing_directory_under_posix(self): """Renaming to an existing directory changes the existing directory under Posix.""" self.check_posix_only() old_path = self.make_path('foo', 'bar') new_path = self.make_path('xyzzy') self.create_dir(self.os.path.join(old_path, 'sub')) self.create_dir(new_path) self.os.rename(old_path.upper(), new_path.upper()) self.assertTrue( self.os.path.exists(self.os.path.join(new_path, 'sub'))) self.assertFalse(self.os.path.exists(old_path)) def test_rename_file_to_existing_directory_raises_under_posix(self): self.check_posix_only() file_path = self.make_path('foo', 'bar', 'baz') new_path = self.make_path('xyzzy') self.create_file(file_path) self.create_dir(new_path) self.assert_raises_os_error(errno.EISDIR, self.os.rename, file_path.upper(), new_path.upper()) def test_rename_to_existent_file_posix(self): """Can rename a file to a used name under Unix.""" self.check_posix_only() directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path, contents='test contents 1') self.create_file(new_file_path, contents='test contents 2') self.assertTrue(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.os.rename(old_file_path.upper(), new_file_path.upper()) self.assertFalse(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.check_contents(new_file_path, 'test contents 1') def test_rename_to_existent_file_windows(self): """Renaming a file to a used name raises OSError under Windows.""" self.check_windows_only() directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path, contents='test contents 1') self.create_file(new_file_path, contents='test contents 2') self.assertTrue(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.assert_raises_os_error(errno.EEXIST, self.os.rename, old_file_path.upper(), new_file_path.upper()) def test_replace_to_existent_file(self): """Replaces an existing file (does not work with `rename()` under Windows).""" directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join(directory, 'plugh_new') self.create_file(old_file_path, contents='test contents 1') self.create_file(new_file_path, contents='test contents 2') self.assertTrue(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.os.replace(old_file_path.upper(), new_file_path.upper()) self.assertFalse(self.os.path.exists(old_file_path)) self.assertTrue(self.os.path.exists(new_file_path)) self.check_contents(new_file_path, 'test contents 1') def test_rename_to_nonexistent_dir(self): """Can rename a file to a name in a nonexistent dir.""" directory = self.make_path('xyzzy') old_file_path = self.os.path.join(directory, 'plugh_old') new_file_path = self.os.path.join( directory, 'no_such_path', 'plugh_new') self.create_file(old_file_path, contents='test contents') self.assertTrue(self.os.path.exists(old_file_path)) self.assertFalse(self.os.path.exists(new_file_path)) self.assert_raises_os_error(errno.ENOENT, self.os.rename, old_file_path.upper(), new_file_path.upper()) self.assertTrue(self.os.path.exists(old_file_path)) self.assertFalse(self.os.path.exists(new_file_path)) self.check_contents(old_file_path, 'test contents') def check_rename_case_only_with_symlink_parent(self): # Regression test for #319 self.os.symlink(self.base_path, self.make_path('link')) dir_upper = self.make_path('link', 'Alpha') self.os.mkdir(dir_upper) dir_lower = self.make_path('alpha') self.os.rename(dir_upper, dir_lower) self.assertEqual(['alpha', 'link'], sorted(self.os.listdir(self.base_path))) def test_rename_case_only_with_symlink_parent_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() self.check_rename_case_only_with_symlink_parent() def test_rename_case_only_with_symlink_parent_macos(self): self.check_macos_only() self.check_rename_case_only_with_symlink_parent() def test_rename_dir(self): """Test a rename of a directory.""" directory = self.make_path('xyzzy') before_dir = self.os.path.join(directory, 'before') before_file = self.os.path.join(directory, 'before', 'file') after_dir = self.os.path.join(directory, 'after') after_file = self.os.path.join(directory, 'after', 'file') self.create_dir(before_dir) self.create_file(before_file, contents='payload') self.assertTrue(self.os.path.exists(before_dir.upper())) self.assertTrue(self.os.path.exists(before_file.upper())) self.assertFalse(self.os.path.exists(after_dir.upper())) self.assertFalse(self.os.path.exists(after_file.upper())) self.os.rename(before_dir.upper(), after_dir) self.assertFalse(self.os.path.exists(before_dir.upper())) self.assertFalse(self.os.path.exists(before_file.upper())) self.assertTrue(self.os.path.exists(after_dir.upper())) self.assertTrue(self.os.path.exists(after_file.upper())) self.check_contents(after_file, 'payload') def test_rename_same_filenames(self): """Test renaming when old and new names are the same.""" directory = self.make_path('xyzzy') file_contents = 'Spam eggs' file_path = self.os.path.join(directory, 'eggs') self.create_file(file_path, contents=file_contents) self.os.rename(file_path, file_path.upper()) self.check_contents(file_path, file_contents) def test_rmdir(self): """Can remove a directory.""" directory = self.make_path('xyzzy') sub_dir = self.make_path('xyzzy', 'abccd') other_dir = self.make_path('xyzzy', 'cdeed') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.os.rmdir(directory) self.assertFalse(self.os.path.exists(directory)) self.create_dir(sub_dir) self.create_dir(other_dir) self.os.chdir(sub_dir) self.os.rmdir('../CDEED') self.assertFalse(self.os.path.exists(other_dir)) self.os.chdir('..') self.os.rmdir('AbcCd') self.assertFalse(self.os.path.exists(sub_dir)) def test_rmdir_via_symlink(self): self.check_windows_only() self.skip_if_symlink_not_supported() base_path = self.make_path('foo', 'bar') dir_path = self.os.path.join(base_path, 'alpha') self.create_dir(dir_path) link_path = self.os.path.join(base_path, 'beta') self.os.symlink(base_path, link_path) self.os.rmdir(link_path + '/Alpha') self.assertFalse(self.os.path.exists(dir_path)) def test_remove_dirs_with_non_top_symlink_succeeds(self): self.check_posix_only() dir_path = self.make_path('dir') dir_link = self.make_path('dir_link') self.create_dir(dir_path) self.os.symlink(dir_path, dir_link) dir_in_dir = self.os.path.join(dir_link, 'dir2') self.create_dir(dir_in_dir) self.os.removedirs(dir_in_dir.upper()) self.assertFalse(self.os.path.exists(dir_in_dir)) # ensure that the symlink is not removed self.assertTrue(self.os.path.exists(dir_link)) def test_mkdir_raises_on_symlink_in_posix(self): self.check_posix_only() base_path = self.make_path('foo', 'bar') link_path = self.os.path.join(base_path, 'link_to_dir') dir_path = self.os.path.join(base_path, 'dir') self.create_dir(dir_path) self.os.symlink(dir_path.upper(), link_path.upper()) self.assert_raises_os_error(errno.ENOTDIR, self.os.rmdir, link_path) def test_mkdir_removes_symlink_in_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() base_path = self.make_path('foo', 'bar') link_path = self.os.path.join(base_path, 'link_to_dir') dir_path = self.os.path.join(base_path, 'dir') self.create_dir(dir_path) self.os.symlink(dir_path.upper(), link_path.upper()) self.os.rmdir(link_path) self.assertFalse(self.os.path.exists(link_path)) self.assertTrue(self.os.path.exists(dir_path)) def test_mkdir_raises_if_directory_exists(self): """mkdir raises exception if directory already exists.""" directory = self.make_path('xyzzy') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, directory.upper()) def test_mkdir_raises_if_file_exists(self): """mkdir raises exception if name already exists as a file.""" directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, file_path.upper()) def test_mkdir_raises_if_symlink_exists(self): # Regression test for #309 self.skip_if_symlink_not_supported() path1 = self.make_path('baz') self.os.symlink(path1, path1) path2 = self.make_path('Baz') self.assert_raises_os_error(errno.EEXIST, self.os.mkdir, path2) def check_mkdir_raises_if_parent_is_file(self, error_type): """mkdir raises exception if name already exists as a file.""" directory = self.make_path('xyzzy') file_path = self.os.path.join(directory, 'plugh') self.create_file(file_path) self.assert_raises_os_error(error_type, self.os.mkdir, self.os.path.join(file_path.upper(), 'ff')) def test_mkdir_raises_if_parent_is_file_posix(self): self.check_posix_only() self.check_mkdir_raises_if_parent_is_file(errno.ENOTDIR) def test_mkdir_raises_if_parent_is_file_windows(self): self.check_windows_only() self.check_mkdir_raises_if_parent_is_file(errno.ENOENT) def test_makedirs(self): """makedirs can create a directory even if parent does not exist.""" parent = self.make_path('xyzzy') directory = self.os.path.join(parent, 'foo') self.assertFalse(self.os.path.exists(parent)) self.os.makedirs(directory.upper()) self.assertTrue(self.os.path.exists(directory)) def check_makedirs_raises_if_parent_is_file(self, error_type): """makedirs raises exception if a parent component exists as a file.""" file_path = self.make_path('xyzzy') directory = self.os.path.join(file_path, 'plugh') self.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(error_type, self.os.makedirs, directory.upper()) def test_makedirs_raises_if_parent_is_file_posix(self): self.check_posix_only() self.check_makedirs_raises_if_parent_is_file(errno.ENOTDIR) def test_makedirs_raises_if_parent_is_file_windows(self): self.check_windows_only() self.check_makedirs_raises_if_parent_is_file(errno.ENOENT) def test_makedirs_raises_if_parent_is_broken_link(self): self.check_posix_only() link_path = self.make_path('broken_link') self.os.symlink(self.make_path('bogus'), link_path) self.assert_raises_os_error(errno.ENOENT, self.os.makedirs, self.os.path.join(link_path.upper(), 'newdir')) def test_makedirs_exist_ok(self): """makedirs uses the exist_ok argument""" directory = self.make_path('xyzzy', 'foo') self.create_dir(directory) self.assertTrue(self.os.path.exists(directory)) self.assert_raises_os_error(errno.EEXIST, self.os.makedirs, directory.upper()) self.os.makedirs(directory.upper(), exist_ok=True) self.assertTrue(self.os.path.exists(directory)) # test fsync and fdatasync def test_fsync_pass(self): test_file_path = self.make_path('test_file') self.create_file(test_file_path, contents='dummy file contents') test_file = self.open(test_file_path.upper(), 'r+') test_fd = test_file.fileno() # Test that this doesn't raise anything self.os.fsync(test_fd) # And just for sanity, double-check that this still raises self.assert_raises_os_error(errno.EBADF, self.os.fsync, test_fd + 10) test_file.close() def test_chmod(self): # set up self.check_posix_only() self.skip_real_fs() path = self.make_path('some_file') self.createTestFile(path) # actual tests self.os.chmod(path.upper(), 0o6543) st = self.os.stat(path) self.assert_mode_equal(0o6543, st.st_mode) self.assertTrue(st.st_mode & stat.S_IFREG) self.assertFalse(st.st_mode & stat.S_IFDIR) def test_symlink(self): self.skip_if_symlink_not_supported() file_path = self.make_path('foo', 'bar', 'baz') self.create_dir(self.make_path('foo', 'bar')) self.os.symlink('bogus', file_path.upper()) self.assertTrue(self.os.path.lexists(file_path)) self.assertFalse(self.os.path.exists(file_path)) self.create_file(self.make_path('Foo', 'Bar', 'Bogus')) self.assertTrue(self.os.path.lexists(file_path)) self.assertTrue(self.os.path.exists(file_path)) # hard link related tests def test_link_delete(self): self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') file2_path = self.make_path('test_file2') contents1 = 'abcdef' # Create file self.create_file(file1_path, contents=contents1) # link to second file self.os.link(file1_path.upper(), file2_path) # delete first file self.os.unlink(file1_path) # assert that second file exists, and its contents are the same self.assertTrue(self.os.path.exists(file2_path)) with self.open(file2_path.upper()) as f: self.assertEqual(f.read(), contents1) def test_link_is_existing_file(self): self.skip_if_symlink_not_supported() file_path = self.make_path('foo', 'bar') self.create_file(file_path) self.assert_raises_os_error(errno.EEXIST, self.os.link, file_path.upper(), file_path.upper()) def test_link_is_broken_symlink(self): # Regression test for #311 self.skip_if_symlink_not_supported() self.check_case_insensitive_fs() file_path = self.make_path('baz') self.create_file(file_path) path_lower = self.make_path('foo') self.os.symlink(path_lower, path_lower) path_upper = self.make_path('Foo') self.assert_raises_os_error(errno.EEXIST, self.os.link, file_path, path_upper) def test_link_with_changed_case(self): # Regression test for #312 self.skip_if_symlink_not_supported() self.check_case_insensitive_fs() link_path = self.make_path('link') self.os.symlink(self.base_path, link_path) link_path = self.os.path.join(link_path, 'Link') self.assertTrue(self.os.lstat(link_path)) class RealOsModuleTestCaseInsensitiveFS(FakeOsModuleTestCaseInsensitiveFS): def use_real_fs(self): return True class FakeOsModuleTimeTest(FakeOsModuleTestBase): def test_chmod_st_ctime(self): with self.mock_time(start=200): file_path = 'some_file' self.filesystem.create_file(file_path) self.assertTrue(self.os.path.exists(file_path)) st = self.os.stat(file_path) self.assertEqual(200, st.st_ctime) # tests self.os.chmod(file_path, 0o765) st = self.os.stat(file_path) self.assertEqual(220, st.st_ctime) def test_utime_sets_current_time_if_args_is_none(self): path = self.make_path('some_file') self.createTestFile(path) with self.mock_time(start=200): self.os.utime(path, times=None) st = self.os.stat(path) self.assertEqual(200, st.st_atime) self.assertEqual(200, st.st_mtime) def test_utime_sets_current_time_if_args_is_none_with_floats(self): # we set os.stat_float_times() to False, so atime/ctime/mtime # are converted as ints (seconds since epoch) stat_float_times = fake_filesystem.FakeOsModule.stat_float_times() fake_filesystem.FakeOsModule.stat_float_times(False) try: with self.mock_time(start=200.9124): path = '/some_file' self.createTestFile(path) st = self.os.stat(path) # 200 is the current time established above # (if converted to int) self.assertEqual(200, st.st_atime) self.assertTrue(isinstance(st.st_atime, int)) self.assertEqual(220, st.st_mtime) self.assertTrue(isinstance(st.st_mtime, int)) self.assertEqual(200912400000, st.st_atime_ns) self.assertEqual(220912400000, st.st_mtime_ns) self.assertEqual(220, st.st_mtime) self.assertEqual(240, st.st_ctime) # actual tests self.os.utime(path, times=None) st = self.os.stat(path) self.assertEqual(260, st.st_atime) self.assertTrue(isinstance(st.st_atime, int)) self.assertEqual(260, st.st_mtime) self.assertTrue(isinstance(st.st_mtime, int)) self.assertEqual(260912400000, st.st_atime_ns) self.assertEqual(260912400000, st.st_mtime_ns) finally: fake_filesystem.FakeOsModule.stat_float_times(stat_float_times) def test_utime_sets_current_time_if_args_is_none_with_floats_n_sec(self): stat_float_times = fake_filesystem.FakeOsModule.stat_float_times() fake_filesystem.FakeOsModule.stat_float_times(False) try: with self.mock_time(start=200.9123): path = self.make_path('some_file') self.createTestFile(path) test_file = self.filesystem.get_object(path) st = self.os.stat(path) self.assertEqual(200, st.st_atime) self.assertEqual(220, st.st_mtime) self.assertEqual(240, st.st_ctime) self.assertEqual(240, test_file.st_ctime) self.assertTrue(isinstance(st.st_ctime, int)) self.assertTrue(isinstance(test_file.st_ctime, int)) self.os.stat_float_times(True) # first time float time self.assertEqual(240, st.st_ctime) # st does not change self.assertEqual(240.9123, test_file.st_ctime) # but the file self.assertTrue(isinstance(st.st_ctime, int)) self.assertTrue(isinstance(test_file.st_ctime, float)) self.os.stat_float_times(False) # reverting to int self.assertEqual(240, test_file.st_ctime) self.assertTrue(isinstance(test_file.st_ctime, int)) self.assertEqual(240, st.st_ctime) self.assertTrue(isinstance(st.st_ctime, int)) self.os.stat_float_times(True) st = self.os.stat(path) # float time not converted to int self.assertAlmostEqual(200.9123, st.st_atime) self.assertAlmostEqual(220.9123, st.st_mtime) self.assertAlmostEqual(240.9123, test_file.st_ctime, st.st_ctime) self.os.utime(path, times=None) st = self.os.stat(path) self.assertAlmostEqual(260.9123, st.st_atime) self.assertAlmostEqual(260.9123, st.st_mtime) finally: fake_filesystem.FakeOsModule.stat_float_times(stat_float_times) def test_utime_sets_specified_time(self): # set up path = self.make_path('some_file') self.createTestFile(path) self.os.stat(path) # actual tests self.os.utime(path, times=(1, 2)) st = self.os.stat(path) self.assertEqual(1, st.st_atime) self.assertEqual(2, st.st_mtime) def test_utime_dir(self): # set up path = '/some_dir' self.createTestDirectory(path) # actual tests self.os.utime(path, times=(1.0, 2.0)) st = self.os.stat(path) self.assertEqual(1.0, st.st_atime) self.assertEqual(2.0, st.st_mtime) def test_utime_follow_symlinks(self): path = self.make_path('some_file') self.createTestFile(path) link_path = '/link_to_some_file' self.filesystem.create_symlink(link_path, path) self.os.utime(link_path, times=(1, 2)) st = self.os.stat(link_path) self.assertEqual(1, st.st_atime) self.assertEqual(2, st.st_mtime) def test_utime_no_follow_symlinks(self): path = self.make_path('some_file') self.createTestFile(path) link_path = '/link_to_some_file' self.filesystem.create_symlink(link_path, path) self.os.utime(link_path, times=(1, 2), follow_symlinks=False) st = self.os.stat(link_path) self.assertNotEqual(1, st.st_atime) self.assertNotEqual(2, st.st_mtime) st = self.os.stat(link_path, follow_symlinks=False) self.assertEqual(1, st.st_atime) self.assertEqual(2, st.st_mtime) def test_utime_non_existent(self): path = '/non/existent/file' self.assertFalse(self.os.path.exists(path)) self.assert_raises_os_error(errno.ENOENT, self.os.utime, path, (1, 2)) def test_utime_invalid_times_arg_raises(self): path = '/some_dir' self.createTestDirectory(path) # the error message differs with different Python versions # we don't expect the same message here self.assertRaises(TypeError, self.os.utime, path, (1, 2, 3)) self.assertRaises(TypeError, self.os.utime, path, (1, 'str')) def test_utime_sets_specified_time_in_ns(self): # set up path = self.make_path('some_file') self.createTestFile(path) self.os.stat(path) # actual tests self.os.utime(path, ns=(200000000, 400000000)) st = self.os.stat(path) self.assertEqual(0.2, st.st_atime) self.assertEqual(0.4, st.st_mtime) def test_utime_incorrect_ns_argument_raises(self): file_path = 'some_file' self.filesystem.create_file(file_path) self.assertRaises(TypeError, self.os.utime, file_path, ns=200000000) self.assertRaises(TypeError, self.os.utime, file_path, ns=('a', 'b')) self.assertRaises(ValueError, self.os.utime, file_path, times=(1, 2), ns=(100, 200)) def test_utime_uses_open_fd_as_path(self): if os.utime not in os.supports_fd: self.skip_real_fs() self.assert_raises_os_error(errno.EBADF, self.os.utime, 5, (1, 2)) path = self.make_path('some_file') self.createTestFile(path) with FakeFileOpen(self.filesystem)(path) as f: self.os.utime(f.filedes, times=(1, 2)) st = self.os.stat(path) self.assertEqual(1, st.st_atime) self.assertEqual(2, st.st_mtime) class FakeOsModuleLowLevelFileOpTest(FakeOsModuleTestBase): """Test low level functions `os.open()`, `os.read()` and `os.write()`.""" def setUp(self): os.umask(0o022) super(FakeOsModuleLowLevelFileOpTest, self).setUp() def test_open_read_only(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_RDONLY) self.assertEqual(b'contents', self.os.read(file_des, 8)) self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b'test') self.os.close(file_des) def test_open_read_only_write_zero_bytes_posix(self): self.check_posix_only() file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_RDONLY) self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b'test') self.os.close(file_des) def test_open_read_only_write_zero_bytes_windows(self): # under Windows, writing an empty string to a read only file # is not an error self.check_windows_only() file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_RDONLY) self.assertEqual(0, self.os.write(file_des, b'')) self.os.close(file_des) def test_open_write_only(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_WRONLY) self.assertEqual(4, self.os.write(file_des, b'test')) self.check_contents(file_path, b'testents') self.os.close(file_des) def test_open_write_only_raises_on_read(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_WRONLY) self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5) self.os.close(file_des) file_des = self.os.open(file_path, os.O_WRONLY | os.O_TRUNC) self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5) self.os.close(file_des) file_path2 = self.make_path('file2') file_des = self.os.open(file_path2, os.O_CREAT | os.O_WRONLY) self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5) self.os.close(file_des) file_des = self.os.open(file_path2, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5) self.os.close(file_des) def test_open_write_only_read_zero_bytes_posix(self): self.check_posix_only() file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY) self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 0) self.os.close(file_des) def test_open_write_only_read_zero_bytes_windows(self): # under Windows, reading 0 bytes from a write only file is not an error self.check_windows_only() file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY) self.assertEqual(b'', self.os.read(file_des, 0)) self.os.close(file_des) def test_open_read_write(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_RDWR) self.assertEqual(4, self.os.write(file_des, b'test')) self.check_contents(file_path, b'testents') self.os.close(file_des) def test_open_create_is_read_only(self): file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_CREAT) self.assertEqual(b'', self.os.read(file_des, 1)) self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b'foo') self.os.close(file_des) def test_open_create_truncate_is_read_only(self): file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_CREAT | os.O_TRUNC) self.assertEqual(b'', self.os.read(file_des, 1)) self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b'foo') self.os.close(file_des) def test_open_raises_if_does_not_exist(self): file_path = self.make_path('file1') self.assert_raises_os_error(errno.ENOENT, self.os.open, file_path, os.O_RDONLY) self.assert_raises_os_error(errno.ENOENT, self.os.open, file_path, os.O_WRONLY) self.assert_raises_os_error(errno.ENOENT, self.os.open, file_path, os.O_RDWR) def test_exclusive_open_raises_without_create_mode(self): self.skip_real_fs() file_path = self.make_path('file1') self.assertRaises(NotImplementedError, self.os.open, file_path, os.O_EXCL) self.assertRaises(NotImplementedError, self.os.open, file_path, os.O_EXCL | os.O_WRONLY) self.assertRaises(NotImplementedError, self.os.open, file_path, os.O_EXCL | os.O_RDWR) self.assertRaises(NotImplementedError, self.os.open, file_path, os.O_EXCL | os.O_TRUNC | os.O_APPEND) def test_open_raises_if_parent_does_not_exist(self): path = self.make_path('alpha', 'alpha') self.assert_raises_os_error(errno.ENOENT, self.os.open, path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) def test_open_truncate(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_RDWR | os.O_TRUNC) self.assertEqual(b'', self.os.read(file_des, 8)) self.assertEqual(4, self.os.write(file_des, b'test')) self.check_contents(file_path, b'test') self.os.close(file_des) @unittest.skipIf(not TestCase.is_windows, 'O_TEMPORARY only present in Windows') def test_temp_file(self): file_path = self.make_path('file1') fd = self.os.open(file_path, os.O_CREAT | os.O_RDWR | os.O_TEMPORARY) self.assertTrue(self.os.path.exists(file_path)) self.os.close(fd) self.assertFalse(self.os.path.exists(file_path)) def test_open_append(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') file_des = self.os.open(file_path, os.O_WRONLY | os.O_APPEND) self.assertEqual(4, self.os.write(file_des, b'test')) self.check_contents(file_path, b'contentstest') self.os.close(file_des) def test_open_create(self): file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_RDWR | os.O_CREAT) self.assertTrue(self.os.path.exists(file_path)) self.assertEqual(4, self.os.write(file_des, b'test')) self.check_contents(file_path, 'test') self.os.close(file_des) def test_can_read_after_create_exclusive(self): self.check_posix_only() path1 = self.make_path('alpha') file_des = self.os.open(path1, os.O_CREAT | os.O_EXCL) self.assertEqual(b'', self.os.read(file_des, 0)) self.assert_raises_os_error(errno.EBADF, self.os.write, file_des, b'') self.os.close(file_des) def test_open_create_mode_posix(self): self.check_posix_only() file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o700) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5) self.assertEqual(4, self.os.write(file_des, b'test')) self.assert_mode_equal(0o700, self.os.stat(file_path).st_mode) self.os.close(file_des) def test_open_create_mode_windows(self): self.check_windows_only() file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o700) self.assertTrue(self.os.path.exists(file_path)) self.assert_raises_os_error(errno.EBADF, self.os.read, file_des, 5) self.assertEqual(4, self.os.write(file_des, b'test')) self.assert_mode_equal(0o666, self.os.stat(file_path).st_mode) self.os.close(file_des) def testOpenCreateMode444Windows(self): self.check_windows_only() file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o442) self.assert_mode_equal(0o444, self.os.stat(file_path).st_mode) self.os.close(file_des) self.os.chmod(file_path, 0o666) def testOpenCreateMode666Windows(self): self.check_windows_only() file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_WRONLY | os.O_CREAT, 0o224) self.assert_mode_equal(0o666, self.os.stat(file_path).st_mode) self.os.close(file_des) def test_open_exclusive(self): file_path = self.make_path('file1') file_des = self.os.open(file_path, os.O_RDWR | os.O_EXCL | os.O_CREAT) self.assertTrue(self.os.path.exists(file_path)) self.os.close(file_des) def test_open_exclusive_raises_if_file_exists(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'contents') self.assert_raises_os_error(errno.EEXIST, self.os.open, file_path, os.O_RDWR | os.O_EXCL | os.O_CREAT) self.assert_raises_os_error(errno.EEXIST, self.os.open, file_path, os.O_RDWR | os.O_EXCL | os.O_CREAT) def test_open_exclusive_raises_if_symlink_exists_in_posix(self): self.check_posix_only() link_path = self.make_path('link') link_target = self.make_path('link_target') self.os.symlink(link_target, link_path) self.assert_raises_os_error( errno.EEXIST, self.os.open, link_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC | os.O_EXCL) def test_open_exclusive_if_symlink_exists_works_in_windows(self): self.check_windows_only() self.skip_if_symlink_not_supported() link_path = self.make_path('link') link_target = self.make_path('link_target') self.os.symlink(link_target, link_path) fd = self.os.open(link_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC | os.O_EXCL) self.os.close(fd) def test_open_directory_raises_under_windows(self): self.check_windows_only() dir_path = self.make_path('dir') self.create_dir(dir_path) self.assert_raises_os_error(errno.EACCES, self.os.open, dir_path, os.O_RDONLY) self.assert_raises_os_error(errno.EACCES, self.os.open, dir_path, os.O_WRONLY) self.assert_raises_os_error(errno.EACCES, self.os.open, dir_path, os.O_RDWR) def test_open_directory_for_writing_raises_under_posix(self): self.check_posix_only() dir_path = self.make_path('dir') self.create_dir(dir_path) self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_WRONLY) self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_RDWR) def test_open_directory_read_only_under_posix(self): self.check_posix_only() self.skip_real_fs() dir_path = self.make_path('dir') self.create_dir(dir_path) file_des = self.os.open(dir_path, os.O_RDONLY) self.assertEqual(3, file_des) self.os.close(file_des) def test_opening_existing_directory_in_creation_mode(self): self.check_linux_only() dir_path = self.make_path("alpha") self.os.mkdir(dir_path) self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_CREAT) def test_writing_to_existing_directory(self): self.check_macos_only() dir_path = self.make_path("alpha") self.os.mkdir(dir_path) fd = self.os.open(dir_path, os.O_CREAT) self.assert_raises_os_error(errno.EBADF, self.os.write, fd, b'') def test_opening_existing_directory_in_write_mode(self): self.check_posix_only() dir_path = self.make_path("alpha") self.os.mkdir(dir_path) self.assert_raises_os_error(errno.EISDIR, self.os.open, dir_path, os.O_WRONLY) def test_open_mode_posix(self): self.check_posix_only() self.skip_real_fs() file_path = self.make_path('baz') file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) stat0 = self.os.fstat(file_des) # not a really good test as this replicates the code, # but we don't know the umask at the test system self.assertEqual(0o100777 & ~self.os._umask(), stat0.st_mode) self.os.close(file_des) def test_open_mode_windows(self): self.check_windows_only() file_path = self.make_path('baz') file_des = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) stat0 = self.os.fstat(file_des) self.assertEqual(0o100666, stat0.st_mode) self.os.close(file_des) def test_write_read(self): file_path = self.make_path('file1') self.create_file(file_path, contents=b'orig contents') new_contents = b'1234567890abcdef' with self.open(file_path, 'wb') as fh: fileno = fh.fileno() self.assertEqual(len(new_contents), self.os.write(fileno, new_contents)) self.check_contents(file_path, new_contents) with self.open(file_path, 'rb') as fh: fileno = fh.fileno() self.assertEqual(b'', self.os.read(fileno, 0)) self.assertEqual(new_contents[0:2], self.os.read(fileno, 2)) self.assertEqual(new_contents[2:10], self.os.read(fileno, 8)) self.assertEqual(new_contents[10:], self.os.read(fileno, 100)) self.assertEqual(b'', self.os.read(fileno, 10)) self.assert_raises_os_error(errno.EBADF, self.os.write, fileno, new_contents) self.assert_raises_os_error(errno.EBADF, self.os.read, fileno, 10) def test_write_from_different_f_ds(self): # Regression test for #211 file_path = self.make_path('baz') fd0 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) fd1 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) self.os.write(fd0, b'aaaa') self.os.write(fd1, b'bb') self.assertEqual(4, self.os.path.getsize(file_path)) self.check_contents(file_path, b'bbaa') self.os.close(fd1) self.os.close(fd0) def test_write_from_different_fds_with_append(self): # Regression test for #268 file_path = self.make_path('baz') fd0 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) fd1 = self.os.open(file_path, os.O_WRONLY | os.O_APPEND) self.os.write(fd0, b'aaa') self.os.write(fd1, b'bbb') self.assertEqual(6, self.os.path.getsize(file_path)) self.check_contents(file_path, b'aaabbb') self.os.close(fd1) self.os.close(fd0) def test_read_only_read_after_write(self): # Regression test for #269 self.check_posix_only() file_path = self.make_path('foo', 'bar', 'baz') self.create_file(file_path, contents=b'test') fd0 = self.os.open(file_path, os.O_CREAT) fd1 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) self.assertEqual(b'', self.os.read(fd0, 0)) self.os.close(fd1) self.os.close(fd0) def test_read_after_closing_write_descriptor(self): # Regression test for #271 file_path = self.make_path('baz') fd0 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) fd1 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) fd2 = self.os.open(file_path, os.O_CREAT) self.os.write(fd1, b'abc') self.os.close(fd0) self.assertEqual(b'abc', self.os.read(fd2, 3)) self.os.close(fd2) self.os.close(fd1) def test_writing_behind_end_of_file(self): # Regression test for #273 file_path = self.make_path('baz') fd1 = self.os.open(file_path, os.O_CREAT) fd2 = self.os.open(file_path, os.O_RDWR) self.os.write(fd2, b'm') fd3 = self.os.open(file_path, os.O_CREAT | os.O_WRONLY | os.O_TRUNC) self.assertEqual(b'', self.os.read(fd2, 1)) self.os.write(fd2, b'm') self.assertEqual(b'\x00m', self.os.read(fd1, 2)) self.os.close(fd1) self.os.close(fd2) self.os.close(fd3) def test_devnull_posix(self): self.check_posix_only() self.assertTrue(self.os.path.exists(self.os.devnull)) def test_devnull_windows(self): self.check_windows_only() if sys.version_info < (3, 8): self.assertFalse(self.os.path.exists(self.os.devnull)) else: self.assertTrue(self.os.path.exists(self.os.devnull)) def test_write_devnull(self): fd = self.os.open(self.os.devnull, os.O_RDWR) self.assertEqual(4, self.os.write(fd, b'test')) self.assertEqual(b'', self.os.read(fd, 4)) self.os.close(fd) fd = self.os.open(self.os.devnull, os.O_RDONLY) self.assertEqual(b'', self.os.read(fd, 4)) self.os.close(fd) def test_sendfile_with_invalid_fd(self): self.check_linux_only() self.assert_raises_os_error(errno.EBADF, self.os.sendfile, 100, 101, 0, 100) src_file_path = self.make_path('foo') dst_file_path = self.make_path('bar') self.create_file(src_file_path, 'testcontent') self.create_file(dst_file_path) fd1 = self.os.open(src_file_path, os.O_RDONLY) fd2 = self.os.open(dst_file_path, os.O_RDONLY) self.assert_raises_os_error(errno.EBADF, self.os.sendfile, fd2, fd1, 0, 4) def test_sendfile_no_offset(self): self.check_linux_only() src_file_path = self.make_path('foo') dst_file_path = self.make_path('bar') self.create_file(src_file_path, 'testcontent') self.create_file(dst_file_path) fd1 = self.os.open(src_file_path, os.O_RDONLY) fd2 = self.os.open(dst_file_path, os.O_RDWR) self.os.sendfile(fd2, fd1, 0, 3) self.os.close(fd2) self.os.close(fd1) with self.open(dst_file_path) as f: self.assertEqual('tes', f.read()) def test_sendfile_with_offset(self): self.check_linux_only() src_file_path = self.make_path('foo') dst_file_path = self.make_path('bar') self.create_file(src_file_path, 'testcontent') self.create_file(dst_file_path) fd1 = self.os.open(src_file_path, os.O_RDONLY) fd2 = self.os.open(dst_file_path, os.O_RDWR) self.os.sendfile(fd2, fd1, 4, 4) self.os.close(fd2) self.os.close(fd1) with self.open(dst_file_path) as f: self.assertEqual('cont', f.read()) def test_sendfile_twice(self): self.check_linux_only() src_file_path = self.make_path('foo') dst_file_path = self.make_path('bar') self.create_file(src_file_path, 'testcontent') self.create_file(dst_file_path) fd1 = self.os.open(src_file_path, os.O_RDONLY) fd2 = self.os.open(dst_file_path, os.O_RDWR) self.os.sendfile(fd2, fd1, 4, 4) self.os.sendfile(fd2, fd1, 4, 4) self.os.close(fd2) self.os.close(fd1) with self.open(dst_file_path) as f: self.assertEqual('contcont', f.read()) def test_sendfile_offset_none(self): self.check_linux_only() src_file_path = self.make_path('foo') dst_file_path = self.make_path('bar') self.create_file(src_file_path, 'testcontent') self.create_file(dst_file_path) fd1 = self.os.open(src_file_path, os.O_RDONLY) fd2 = self.os.open(dst_file_path, os.O_RDWR) self.os.sendfile(fd2, fd1, None, 4) self.os.sendfile(fd2, fd1, None, 3) self.os.close(fd2) self.os.close(fd1) with self.open(dst_file_path) as f: self.assertEqual('testcon', f.read()) @unittest.skipIf(not TestCase.is_macos, 'Testing MacOs only behavior') def test_no_sendfile_to_regular_file_under_macos(self): src_file_path = self.make_path('foo') dst_file_path = self.make_path('bar') self.create_file(src_file_path, 'testcontent') self.create_file(dst_file_path) fd1 = self.os.open(src_file_path, os.O_RDONLY) fd2 = self.os.open(dst_file_path, os.O_RDWR) # raises socket operation on non-socket self.assertRaises(OSError, self.os.sendfile, fd2, fd1, 0, 3) self.os.close(fd2) self.os.close(fd1) class RealOsModuleLowLevelFileOpTest(FakeOsModuleLowLevelFileOpTest): def use_real_fs(self): return True class FakeOsModuleWalkTest(FakeOsModuleTestBase): def assertWalkResults(self, expected, top, topdown=True, followlinks=False): # as the result of walk is unsorted, we have to check against # sorted results result = [step for step in self.os.walk( top, topdown=topdown, followlinks=followlinks)] result = sorted(result, key=lambda lst: lst[0]) expected = sorted(expected, key=lambda lst: lst[0]) self.assertEqual(len(expected), len(result)) for entry, expected_entry in zip(result, expected): self.assertEqual(expected_entry[0], entry[0]) self.assertEqual(expected_entry[1], sorted(entry[1])) self.assertEqual(expected_entry[2], sorted(entry[2])) def ResetErrno(self): """Reset the last seen errno.""" self.last_errno = False def StoreErrno(self, os_error): """Store the last errno we saw.""" self.last_errno = os_error.errno def GetErrno(self): """Return the last errno we saw.""" return self.last_errno def test_walk_top_down(self): """Walk down ordering is correct.""" base_dir = self.make_path('foo') self.create_file(self.os.path.join(base_dir, '1.txt')) self.create_file(self.os.path.join(base_dir, 'bar1', '2.txt')) self.create_file(self.os.path.join(base_dir, 'bar1', 'baz', '3.txt')) self.create_file(self.os.path.join(base_dir, 'bar2', '4.txt')) expected = [ (base_dir, ['bar1', 'bar2'], ['1.txt']), (self.os.path.join(base_dir, 'bar1'), ['baz'], ['2.txt']), (self.os.path.join(base_dir, 'bar1', 'baz'), [], ['3.txt']), (self.os.path.join(base_dir, 'bar2'), [], ['4.txt']), ] self.assertWalkResults(expected, base_dir) def test_walk_bottom_up(self): """Walk up ordering is correct.""" base_dir = self.make_path('foo') self.create_file(self.os.path.join(base_dir, 'bar1', 'baz', '1.txt')) self.create_file(self.os.path.join(base_dir, 'bar1', '2.txt')) self.create_file(self.os.path.join(base_dir, 'bar2', '3.txt')) self.create_file(self.os.path.join(base_dir, '4.txt')) expected = [ (self.os.path.join(base_dir, 'bar1', 'baz'), [], ['1.txt']), (self.os.path.join(base_dir, 'bar1'), ['baz'], ['2.txt']), (self.os.path.join(base_dir, 'bar2'), [], ['3.txt']), (base_dir, ['bar1', 'bar2'], ['4.txt']), ] self.assertWalkResults(expected, self.make_path('foo'), topdown=False) def test_walk_raises_if_non_existent(self): """Raises an exception when attempting to walk non-existent directory.""" directory = self.make_path('foo', 'bar') self.assertEqual(False, self.os.path.exists(directory)) generator = self.os.walk(directory) self.assertRaises(StopIteration, next, generator) def test_walk_raises_if_not_directory(self): """Raises an exception when attempting to walk a non-directory.""" filename = self.make_path('foo', 'bar') self.create_file(filename) generator = self.os.walk(filename) self.assertRaises(StopIteration, next, generator) def test_walk_calls_on_error_if_non_existent(self): """Calls onerror with correct errno when walking non-existent directory.""" self.ResetErrno() directory = self.make_path('foo', 'bar') self.assertEqual(False, self.os.path.exists(directory)) # Calling os.walk on a non-existent directory should trigger # a call to the onerror method. # We do not actually care what, if anything, is returned. for _ in self.os.walk(directory, onerror=self.StoreErrno): pass self.assertTrue(self.GetErrno() in (errno.ENOTDIR, errno.ENOENT)) def test_walk_calls_on_error_if_not_directory(self): """Calls onerror with correct errno when walking non-directory.""" self.ResetErrno() filename = self.make_path('foo' 'bar') self.create_file(filename) self.assertEqual(True, self.os.path.exists(filename)) # Calling `os.walk` on a file should trigger a call to the # `onerror` method. # We do not actually care what, if anything, is returned. for _ in self.os.walk(filename, onerror=self.StoreErrno): pass self.assertTrue(self.GetErrno() in (errno.ENOTDIR, errno.EACCES)) def test_walk_skips_removed_directories(self): """Caller can modify list of directories to visit while walking.""" root = self.make_path('foo') visit = 'visit' no_visit = 'no_visit' self.create_file(self.os.path.join(root, 'bar')) self.create_file(self.os.path.join(root, visit, '1.txt')) self.create_file(self.os.path.join(root, visit, '2.txt')) self.create_file(self.os.path.join(root, no_visit, '3.txt')) self.create_file(self.os.path.join(root, no_visit, '4.txt')) generator = self.os.walk(self.make_path('foo')) root_contents = next(generator) root_contents[1].remove(no_visit) visited_visit_directory = False for root, _dirs, _files in iter(generator): self.assertEqual(False, root.endswith(self.os.path.sep + no_visit)) if root.endswith(self.os.path.sep + visit): visited_visit_directory = True self.assertEqual(True, visited_visit_directory) def test_walk_followsymlink_disabled(self): self.check_posix_only() base_dir = self.make_path('foo') link_dir = self.make_path('linked') self.create_file(self.os.path.join(link_dir, 'subfile')) self.create_file(self.os.path.join(base_dir, 'bar', 'baz')) self.create_file(self.os.path.join(base_dir, 'bar', 'xyzzy', 'plugh')) self.create_symlink( self.os.path.join(base_dir, 'created_link'), link_dir) expected = [ (base_dir, ['bar', 'created_link'], []), (self.os.path.join(base_dir, 'bar'), ['xyzzy'], ['baz']), (self.os.path.join(base_dir, 'bar', 'xyzzy'), [], ['plugh']), ] self.assertWalkResults(expected, base_dir, followlinks=False) expected = [(self.os.path.join(base_dir, 'created_link'), [], ['subfile'])] self.assertWalkResults(expected, self.os.path.join(base_dir, 'created_link'), followlinks=False) def test_walk_followsymlink_enabled(self): self.check_posix_only() base_dir = self.make_path('foo') link_dir = self.make_path('linked') self.create_file(self.os.path.join(link_dir, 'subfile')) self.create_file(self.os.path.join(base_dir, 'bar', 'baz')) self.create_file(self.os.path.join(base_dir, 'bar', 'xyzzy', 'plugh')) self.create_symlink(self.os.path.join(base_dir, 'created_link'), self.os.path.join(link_dir)) expected = [ (base_dir, ['bar', 'created_link'], []), (self.os.path.join(base_dir, 'bar'), ['xyzzy'], ['baz']), (self.os.path.join(base_dir, 'bar', 'xyzzy'), [], ['plugh']), (self.os.path.join(base_dir, 'created_link'), [], ['subfile']), ] self.assertWalkResults(expected, base_dir, followlinks=True) expected = [(self.os.path.join(base_dir, 'created_link'), [], ['subfile'])] self.assertWalkResults(expected, self.os.path.join(base_dir, 'created_link'), followlinks=True) def test_walk_linked_file_in_subdir(self): # regression test for #559 (tested for link on incomplete path) self.check_posix_only() # need to have a top-level link to reproduce the bug - skip real fs self.skip_real_fs() file_path = '/foo/bar/baz' self.create_file(file_path) self.create_symlink('bar', file_path) expected = [ ('/foo', ['bar'], []), ('/foo/bar', [], ['baz']) ] self.assertWalkResults(expected, '/foo') def test_base_dirpath(self): # regression test for #512 file_path = self.make_path('foo', 'bar', 'baz') self.create_file(file_path) variants = [ self.make_path('foo', 'bar'), self.make_path('foo', '..', 'foo', 'bar'), self.make_path('foo', '..', 'foo', 'bar') + self.os.path.sep * 3, self.make_path('foo') + self.os.path.sep * 3 + 'bar' ] for base_dir in variants: for dirpath, dirnames, filenames in self.os.walk(base_dir): self.assertEqual(dirpath, base_dir) file_path = self.make_path('foo', 'bar', 'dir', 'baz') self.create_file(file_path) for base_dir in variants: for dirpath, dirnames, filenames in self.os.walk(base_dir): self.assertTrue(dirpath.startswith(base_dir)) class RealOsModuleWalkTest(FakeOsModuleWalkTest): def use_real_fs(self): return True class FakeOsModuleDirFdTest(FakeOsModuleTestBase): def setUp(self): super(FakeOsModuleDirFdTest, self).setUp() self.os.supports_dir_fd = set() self.filesystem.is_windows_fs = False self.filesystem.create_dir('/foo/bar') self.dir_fd = self.os.open('/foo', os.O_RDONLY) self.filesystem.create_file('/foo/baz') def test_access(self): self.assertRaises( NotImplementedError, self.os.access, 'baz', self.os.F_OK, dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.access) self.assertTrue( self.os.access('baz', self.os.F_OK, dir_fd=self.dir_fd)) def test_chmod(self): self.assertRaises( NotImplementedError, self.os.chmod, 'baz', 0o6543, dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.chmod) self.os.chmod('baz', 0o6543, dir_fd=self.dir_fd) st = self.os.stat('/foo/baz') self.assert_mode_equal(0o6543, st.st_mode) @unittest.skipIf(not hasattr(os, 'chown'), 'chown not on all platforms available') def test_chown(self): self.assertRaises( NotImplementedError, self.os.chown, 'baz', 100, 101, dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.chown) self.os.chown('baz', 100, 101, dir_fd=self.dir_fd) st = self.os.stat('/foo/baz') self.assertEqual(st[stat.ST_UID], 100) self.assertEqual(st[stat.ST_GID], 101) def test_link_src_fd(self): self.assertRaises( NotImplementedError, self.os.link, 'baz', '/bat', src_dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.link) self.os.link('baz', '/bat', src_dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/bat')) def test_link_dst_fd(self): self.assertRaises( NotImplementedError, self.os.link, 'baz', '/bat', dst_dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.link) self.os.link('/foo/baz', 'bat', dst_dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/foo/bat')) def test_symlink(self): self.assertRaises( NotImplementedError, self.os.symlink, 'baz', '/bat', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.symlink) self.os.symlink('baz', '/bat', dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/bat')) def test_readlink(self): self.skip_if_symlink_not_supported() self.filesystem.create_symlink('/meyer/lemon/pie', '/foo/baz') self.filesystem.create_symlink('/geo/metro', '/meyer') self.assertRaises( NotImplementedError, self.os.readlink, '/geo/metro/lemon/pie', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.readlink) self.assertEqual('/foo/baz', self.os.readlink( '/geo/metro/lemon/pie', dir_fd=self.dir_fd)) def test_stat(self): self.assertRaises( NotImplementedError, self.os.stat, 'baz', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.stat) st = self.os.stat('baz', dir_fd=self.dir_fd) self.assertEqual(st.st_mode, 0o100666) def test_lstat(self): self.assertRaises( NotImplementedError, self.os.lstat, 'baz', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.lstat) st = self.os.lstat('baz', dir_fd=self.dir_fd) self.assertEqual(st.st_mode, 0o100666) def test_mkdir(self): self.assertRaises( NotImplementedError, self.os.mkdir, 'newdir', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.mkdir) self.os.mkdir('newdir', dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/foo/newdir')) def test_rmdir(self): self.assertRaises( NotImplementedError, self.os.rmdir, 'bar', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.rmdir) self.os.rmdir('bar', dir_fd=self.dir_fd) self.assertFalse(self.os.path.exists('/foo/bar')) @unittest.skipIf(not hasattr(os, 'mknod'), 'mknod not on all platforms available') def test_mknod(self): self.assertRaises( NotImplementedError, self.os.mknod, 'newdir', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.mknod) self.os.mknod('newdir', dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/foo/newdir')) def test_rename_src_fd(self): self.assertRaises( NotImplementedError, self.os.rename, 'baz', '/foo/batz', src_dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.rename) self.os.rename('bar', '/foo/batz', src_dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/foo/batz')) def test_rename_dst_fd(self): self.assertRaises( NotImplementedError, self.os.rename, 'baz', '/foo/batz', dst_dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.rename) self.os.rename('/foo/bar', 'batz', dst_dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/foo/batz')) def test_replace_src_fd(self): self.assertRaises( NotImplementedError, self.os.rename, 'baz', '/foo/batz', src_dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.rename) self.os.replace('bar', '/foo/batz', src_dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/foo/batz')) def test_replace_dst_fd(self): self.assertRaises( NotImplementedError, self.os.rename, 'baz', '/foo/batz', dst_dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.rename) self.os.replace('/foo/bar', 'batz', dst_dir_fd=self.dir_fd) self.assertTrue(self.os.path.exists('/foo/batz')) def test_remove(self): self.assertRaises( NotImplementedError, self.os.remove, 'baz', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.remove) self.os.remove('baz', dir_fd=self.dir_fd) self.assertFalse(self.os.path.exists('/foo/baz')) def test_unlink(self): self.assertRaises( NotImplementedError, self.os.unlink, 'baz', dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.unlink) self.os.unlink('baz', dir_fd=self.dir_fd) self.assertFalse(self.os.path.exists('/foo/baz')) def test_utime(self): self.assertRaises( NotImplementedError, self.os.utime, 'baz', (1, 2), dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.utime) self.os.utime('baz', times=(1, 2), dir_fd=self.dir_fd) st = self.os.stat('/foo/baz') self.assertEqual(1, st.st_atime) self.assertEqual(2, st.st_mtime) def test_open(self): self.assertRaises( NotImplementedError, self.os.open, 'baz', os.O_RDONLY, dir_fd=self.dir_fd) self.os.supports_dir_fd.add(os.open) fd = self.os.open('baz', os.O_RDONLY, dir_fd=self.dir_fd) self.assertLess(0, fd) class StatPropagationTest(TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem(path_separator='/') self.os = fake_filesystem.FakeOsModule(self.filesystem) self.open = fake_filesystem.FakeFileOpen(self.filesystem) def test_file_size_updated_via_close(self): """test that file size gets updated via close().""" file_dir = 'xyzzy' file_path = 'xyzzy/close' content = 'This is a test.' self.os.mkdir(file_dir) fh = self.open(file_path, 'w') self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual('', self.filesystem.get_object(file_path).contents) fh.write(content) self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual('', self.filesystem.get_object(file_path).contents) fh.close() self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual(content, self.filesystem.get_object(file_path).contents) def test_file_size_not_reset_after_close(self): file_dir = 'xyzzy' file_path = 'xyzzy/close' self.os.mkdir(file_dir) size = 1234 # The file has size, but no content. When the file is opened for # reading, its size should be preserved. self.filesystem.create_file(file_path, st_size=size) fh = self.open(file_path, 'r') fh.close() self.assertEqual(size, self.open(file_path, 'r').size()) def test_file_size_after_write(self): file_path = 'test_file' original_content = 'abcdef' original_size = len(original_content) self.filesystem.create_file(file_path, contents=original_content) added_content = 'foo bar' expected_size = original_size + len(added_content) fh = self.open(file_path, 'a') fh.write(added_content) self.assertEqual(original_size, fh.size()) fh.close() self.assertEqual(expected_size, self.open(file_path, 'r').size()) def test_large_file_size_after_write(self): file_path = 'test_file' original_content = 'abcdef' original_size = len(original_content) self.filesystem.create_file(file_path, st_size=original_size) added_content = 'foo bar' fh = self.open(file_path, 'a') self.assertRaises(fake_filesystem.FakeLargeFileIoException, lambda: fh.write(added_content)) def test_file_size_updated_via_flush(self): """test that file size gets updated via flush().""" file_dir = 'xyzzy' file_name = 'flush' file_path = self.os.path.join(file_dir, file_name) content = 'This might be a test.' self.os.mkdir(file_dir) fh = self.open(file_path, 'w') self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual('', self.filesystem.get_object(file_path).contents) fh.write(content) self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual('', self.filesystem.get_object(file_path).contents) fh.flush() self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual(content, self.filesystem.get_object(file_path).contents) fh.close() self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual(content, self.filesystem.get_object(file_path).contents) def test_file_size_truncation(self): """test that file size gets updated via open().""" file_dir = 'xyzzy' file_path = 'xyzzy/truncation' content = 'AAA content.' # pre-create file with content self.os.mkdir(file_dir) fh = self.open(file_path, 'w') fh.write(content) fh.close() self.assertEqual(len(content), self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual(content, self.filesystem.get_object(file_path).contents) # test file truncation fh = self.open(file_path, 'w') self.assertEqual(0, self.os.stat(file_path)[stat.ST_SIZE]) self.assertEqual('', self.filesystem.get_object(file_path).contents) fh.close() @unittest.skipIf(not use_scandir, 'only run if scandir is available') class FakeScandirTest(FakeOsModuleTestBase): FILE_SIZE = 50 LINKED_FILE_SIZE = 10 def setUp(self): super(FakeScandirTest, self).setUp() self.supports_symlinks = (not self.is_windows or not self.use_real_fs()) if use_scandir_package: if self.use_real_fs(): from scandir import scandir else: import pyfakefs.fake_scandir def fake_scan_dir(p): return pyfakefs.fake_scandir.scandir(self.filesystem, p) scandir = fake_scan_dir else: scandir = self.os.scandir self.scandir = scandir self.directory = self.make_path('xyzzy', 'plugh') link_dir = self.make_path('linked', 'plugh') self.linked_file_path = self.os.path.join(link_dir, 'file') self.linked_dir_path = self.os.path.join(link_dir, 'dir') self.rel_linked_dir_path = ( self.os.path.join('..', '..', 'linked', 'plugh', 'dir')) self.rel_linked_file_path = ( self.os.path.join('..', '..', 'linked', 'plugh', 'file')) self.dir_path = self.os.path.join(self.directory, 'dir') self.file_path = self.os.path.join(self.directory, 'file') self.file_link_path = self.os.path.join(self.directory, 'link_file') self.dir_link_path = self.os.path.join(self.directory, 'link_dir') self.file_rel_link_path = self.os.path.join(self.directory, 'rel_link_file') self.dir_rel_link_path = self.os.path.join(self.directory, 'rel_link_dir') self.create_dir(self.dir_path) self.create_file(self.file_path, contents=b'b' * self.FILE_SIZE) if self.supports_symlinks: self.create_dir(self.linked_dir_path) self.create_file(self.linked_file_path, contents=b'a' * self.LINKED_FILE_SIZE), self.create_symlink(self.dir_link_path, self.linked_dir_path) self.create_symlink(self.file_link_path, self.linked_file_path) self.create_symlink(self.dir_rel_link_path, self.rel_linked_dir_path) self.create_symlink(self.file_rel_link_path, self.rel_linked_file_path) # Changing the working directory below is to make sure relative paths # to the files and directories created above are reasonable. # Corner-cases about relative paths are better checked in tests created # for that purpose. # # WARNING: This is self.pretest_cwd and not self.cwd as the latter is # used by superclass RealFsTestCase. self.pretest_cwd = self.os.getcwd() self.os.chdir(self.base_path) self.dir_entries = list(self.do_scandir()) self.dir_entries.sort(key=lambda entry: entry.name) def tearDown(self): self.os.chdir(self.pretest_cwd) super().tearDown() def do_scandir(self): """Hook to override how scandir is called.""" return self.scandir(self.directory) def scandir_path(self): """Hook to override the expected scandir() path in DirEntry.path.""" return self.directory def test_paths(self): sorted_names = ['dir', 'file'] if self.supports_symlinks: sorted_names.extend(['link_dir', 'link_file', 'rel_link_dir', 'rel_link_file']) self.assertEqual(len(sorted_names), len(self.dir_entries)) self.assertEqual(sorted_names, [entry.name for entry in self.dir_entries]) sorted_paths = [self.os.path.join(self.scandir_path(), name) for name in sorted_names] self.assertEqual(sorted_paths, [entry.path for entry in self.dir_entries]) def test_isfile(self): self.assertFalse(self.dir_entries[0].is_file()) self.assertTrue(self.dir_entries[1].is_file()) if self.supports_symlinks: self.assertFalse(self.dir_entries[2].is_file()) self.assertFalse( self.dir_entries[2].is_file(follow_symlinks=False)) self.assertTrue(self.dir_entries[3].is_file()) self.assertFalse( self.dir_entries[3].is_file(follow_symlinks=False)) self.assertFalse(self.dir_entries[4].is_file()) self.assertFalse( self.dir_entries[4].is_file(follow_symlinks=False)) self.assertTrue(self.dir_entries[5].is_file()) self.assertFalse( self.dir_entries[5].is_file(follow_symlinks=False)) def test_isdir(self): self.assertTrue(self.dir_entries[0].is_dir()) self.assertFalse(self.dir_entries[1].is_dir()) if self.supports_symlinks: self.assertTrue(self.dir_entries[2].is_dir()) self.assertFalse(self.dir_entries[2].is_dir(follow_symlinks=False)) self.assertFalse(self.dir_entries[3].is_dir()) self.assertFalse(self.dir_entries[3].is_dir(follow_symlinks=False)) self.assertTrue(self.dir_entries[4].is_dir()) self.assertFalse(self.dir_entries[4].is_dir(follow_symlinks=False)) self.assertFalse(self.dir_entries[5].is_dir()) self.assertFalse(self.dir_entries[5].is_dir(follow_symlinks=False)) def test_is_link(self): if self.supports_symlinks: self.assertFalse(self.dir_entries[0].is_symlink()) self.assertFalse(self.dir_entries[1].is_symlink()) self.assertTrue(self.dir_entries[2].is_symlink()) self.assertTrue(self.dir_entries[3].is_symlink()) self.assertTrue(self.dir_entries[4].is_symlink()) self.assertTrue(self.dir_entries[5].is_symlink()) def test_path_links_not_resolved(self): # regression test for #350 self.skip_if_symlink_not_supported() dir_path = self.make_path('A', 'B', 'C') self.os.makedirs(self.os.path.join(dir_path, 'D')) link_path = self.make_path('A', 'C') self.os.symlink(dir_path, link_path) self.assertEqual([self.os.path.join(link_path, 'D')], [f.path for f in self.scandir(link_path)]) def test_inode(self): if use_scandir and self.use_real_fs(): if self.is_windows: self.skipTest( 'inode seems not to work in scandir module under Windows') if IN_DOCKER: self.skipTest( 'inode seems not to work in a Docker container') self.assertEqual(self.os.stat(self.dir_path).st_ino, self.dir_entries[0].inode()) self.assertEqual(self.os.stat(self.file_path).st_ino, self.dir_entries[1].inode()) if self.supports_symlinks: self.assertEqual(self.os.lstat(self.dir_link_path).st_ino, self.dir_entries[2].inode()) self.assertEqual(self.os.lstat(self.file_link_path).st_ino, self.dir_entries[3].inode()) self.assertEqual(self.os.lstat(self.dir_rel_link_path).st_ino, self.dir_entries[4].inode()) self.assertEqual(self.os.lstat(self.file_rel_link_path).st_ino, self.dir_entries[5].inode()) def test_scandir_stat_nlink(self): # regression test for #350 stat_nlink = self.os.stat(self.file_path).st_nlink self.assertEqual(1, stat_nlink) dir_iter = self.scandir(self.directory) for item in dir_iter: if item.path == self.file_path: scandir_stat_nlink = item.stat().st_nlink if self.is_windows_fs: self.assertEqual(0, scandir_stat_nlink) else: self.assertEqual(1, scandir_stat_nlink) self.assertEqual(1, self.os.stat(self.file_path).st_nlink) def check_stat(self, absolute_symlink_expected_size, relative_symlink_expected_size): self.assertEqual(self.FILE_SIZE, self.dir_entries[1].stat().st_size) self.assertEqual( int(self.os.stat(self.dir_path).st_ctime), int(self.dir_entries[0].stat().st_ctime)) if self.supports_symlinks: self.assertEqual(self.LINKED_FILE_SIZE, self.dir_entries[3].stat().st_size) self.assertEqual(absolute_symlink_expected_size, self.dir_entries[3].stat( follow_symlinks=False).st_size) self.assertEqual( int(self.os.stat(self.linked_dir_path).st_mtime), int(self.dir_entries[2].stat().st_mtime)) self.assertEqual(self.LINKED_FILE_SIZE, self.dir_entries[5].stat().st_size) self.assertEqual(relative_symlink_expected_size, self.dir_entries[5].stat( follow_symlinks=False).st_size) self.assertEqual( int(self.os.stat(self.linked_dir_path).st_mtime), int(self.dir_entries[4].stat().st_mtime)) @unittest.skipIf(TestCase.is_windows, 'POSIX specific behavior') def test_stat_posix(self): self.check_stat(len(self.linked_file_path), len(self.rel_linked_file_path)) @unittest.skipIf(not TestCase.is_windows, 'Windows specific behavior') def test_stat_windows(self): self.check_stat(0, 0) def test_index_access_to_stat_times_returns_int(self): self.assertEqual(self.os.stat(self.dir_path)[stat.ST_CTIME], int(self.dir_entries[0].stat().st_ctime)) if self.supports_symlinks: self.assertEqual(self.os.stat(self.linked_dir_path)[stat.ST_MTIME], int(self.dir_entries[2].stat().st_mtime)) self.assertEqual(self.os.stat(self.linked_dir_path)[stat.ST_MTIME], int(self.dir_entries[4].stat().st_mtime)) def test_stat_ino_dev(self): if self.supports_symlinks: file_stat = self.os.stat(self.linked_file_path) self.assertEqual(file_stat.st_ino, self.dir_entries[3].stat().st_ino) self.assertEqual(file_stat.st_dev, self.dir_entries[3].stat().st_dev) self.assertEqual(file_stat.st_ino, self.dir_entries[5].stat().st_ino) self.assertEqual(file_stat.st_dev, self.dir_entries[5].stat().st_dev) @unittest.skipIf(sys.version_info < (3, 6) or not use_builtin_scandir, 'Path-like objects have been introduced in Python 3.6') def test_path_like(self): self.assertTrue(isinstance(self.dir_entries[0], os.PathLike)) self.assertEqual(self.os.path.join(self.scandir_path(), 'dir'), os.fspath(self.dir_entries[0])) self.assertEqual(self.os.path.join(self.scandir_path(), 'file'), os.fspath(self.dir_entries[1])) def test_non_existing_dir(self): # behaves differently in different systems, so we skip the real fs test self.skip_real_fs() self.assert_raises_os_error( errno.ENOENT, self.scandir, 'non_existing/fake_dir') class RealScandirTest(FakeScandirTest): def use_real_fs(self): return True class FakeScandirRelTest(FakeScandirTest): def scandir_path(self): # When scandir is called with a relative path, that relative path is # used in the path attribute of the DirEntry objects. return self.os.path.relpath(self.directory) def do_scandir(self): return self.scandir(self.os.path.relpath(self.directory)) class RealScandirRelTest(FakeScandirRelTest): def use_real_fs(self): return True @unittest.skipIf(sys.version_info < (3, 7) or TestCase.is_windows or use_scandir_package, 'dir_fd support for os.scandir was introduced in Python 3.7') class FakeScandirFdTest(FakeScandirTest): def tearDown(self): self.os.close(self.dir_fd) super(FakeScandirFdTest, self).tearDown() def scandir_path(self): # When scandir is called with a filedescriptor, only the name of the # entry is returned in the path attribute of the DirEntry objects. return '' def do_scandir(self): self.dir_fd = self.os.open(self.directory, os.O_RDONLY) return self.scandir(self.dir_fd) class RealScandirFdTest(FakeScandirFdTest): def use_real_fs(self): return True class FakeScandirFdRelTest(FakeScandirFdTest): def do_scandir(self): self.dir_fd = self.os.open(self.os.path.relpath(self.directory), os.O_RDONLY) return self.scandir(self.dir_fd) class RealScandirFdRelTest(FakeScandirFdRelTest): def use_real_fs(self): return True class FakeExtendedAttributeTest(FakeOsModuleTestBase): def setUp(self): super(FakeExtendedAttributeTest, self).setUp() self.check_linux_only() self.dir_path = self.make_path('foo') self.file_path = self.os.path.join(self.dir_path, 'bar') self.create_file(self.file_path) def test_empty_xattr(self): self.assertEqual([], self.os.listxattr(self.dir_path)) self.assertEqual([], self.os.listxattr(self.file_path)) def test_setxattr(self): self.assertRaises(TypeError, self.os.setxattr, self.file_path, 'test', 'value') self.assert_raises_os_error(errno.EEXIST, self.os.setxattr, self.file_path, 'test', b'value', self.os.XATTR_REPLACE) self.os.setxattr(self.file_path, 'test', b'value') self.assertEqual(b'value', self.os.getxattr(self.file_path, 'test')) self.assert_raises_os_error(errno.ENODATA, self.os.setxattr, self.file_path, 'test', b'value', self.os.XATTR_CREATE) def test_removeattr(self): self.os.removexattr(self.file_path, 'test') self.assertEqual([], self.os.listxattr(self.file_path)) self.os.setxattr(self.file_path, b'test', b'value') self.assertEqual(['test'], self.os.listxattr(self.file_path)) self.assertEqual(b'value', self.os.getxattr(self.file_path, 'test')) self.os.removexattr(self.file_path, 'test') self.assertEqual([], self.os.listxattr(self.file_path)) self.assertIsNone(self.os.getxattr(self.file_path, 'test')) def test_default_path(self): self.os.chdir(self.dir_path) self.os.setxattr(self.dir_path, b'test', b'value') self.assertEqual(['test'], self.os.listxattr()) self.assertEqual(b'value', self.os.getxattr(self.dir_path, 'test')) class FakeOsUnreadableDirTest(FakeOsModuleTestBase): def setUp(self): if self.use_real_fs(): # make sure no dir is created if skipped self.check_posix_only() super(FakeOsUnreadableDirTest, self).setUp() self.check_posix_only() self.dir_path = self.make_path('some_dir') self.file_path = self.os.path.join(self.dir_path, 'some_file') self.create_file(self.file_path) self.os.chmod(self.dir_path, 0o000) def test_listdir_unreadable_dir(self): if not is_root(): self.assert_raises_os_error( errno.EACCES, self.os.listdir, self.dir_path) else: self.assertEqual(['some_file'], self.os.listdir(self.dir_path)) def test_stat_unreadable_dir(self): self.assertEqual(0, self.os.stat(self.dir_path).st_mode & 0o666) def test_stat_file_in_unreadable_dir(self): if not is_root(): self.assert_raises_os_error( errno.EACCES, self.os.stat, self.file_path) else: self.assertEqual(0, self.os.stat(self.file_path).st_size) class RealOsUnreadableDirTest(FakeOsUnreadableDirTest): def use_real_fs(self): return True pyfakefs-4.5.4/pyfakefs/tests/fake_pathlib_test.py0000666000000000000000000014052514147521400020467 0ustar 00000000000000# -*- coding: utf-8 -*- # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Unittests for fake_pathlib. As most of fake_pathlib is a wrapper around fake_filesystem methods, the tests are there mostly to ensure basic functionality. Note that many of the tests are directly taken from examples in the python docs. """ import errno import os import pathlib import stat import sys import unittest from pyfakefs.fake_filesystem import is_root from pyfakefs import fake_pathlib, fake_filesystem from pyfakefs.helpers import IS_PYPY from pyfakefs.tests.test_utils import RealFsTestCase is_windows = sys.platform == 'win32' class RealPathlibTestCase(RealFsTestCase): def __init__(self, methodName='runTest'): super(RealPathlibTestCase, self).__init__(methodName) self.pathlib = pathlib self.path = None def setUp(self): super().setUp() if not self.use_real_fs(): self.pathlib = fake_pathlib.FakePathlibModule(self.filesystem) self.path = self.pathlib.Path class FakePathlibInitializationTest(RealPathlibTestCase): def test_initialization_type(self): """Make sure tests for class type will work""" path = self.path('/test') if is_windows: self.assertTrue(isinstance(path, self.pathlib.WindowsPath)) self.assertTrue(isinstance(path, self.pathlib.PureWindowsPath)) self.assertTrue(self.pathlib.PurePosixPath()) # in fake fs, we allow to use the other OS implementation if self.use_real_fs(): with self.assertRaises(NotImplementedError): self.pathlib.PosixPath() else: self.assertTrue(self.pathlib.PosixPath()) else: self.assertTrue(isinstance(path, self.pathlib.PosixPath)) self.assertTrue(isinstance(path, self.pathlib.PurePosixPath)) self.assertTrue(self.pathlib.PureWindowsPath()) if self.use_real_fs(): with self.assertRaises(NotImplementedError): self.pathlib.WindowsPath() else: self.assertTrue(self.pathlib.WindowsPath()) def test_init_with_segments(self): """Basic initialization tests - taken from pathlib.Path documentation """ self.assertEqual(self.path('/', 'foo', 'bar', 'baz'), self.path('/foo/bar/baz')) self.assertEqual(self.path(), self.path('.')) self.assertEqual(self.path(self.path('foo'), self.path('bar')), self.path('foo/bar')) self.assertEqual(self.path('/etc') / 'init.d' / 'reboot', self.path('/etc/init.d/reboot')) def test_init_collapse(self): """Tests for collapsing path during initialization. Taken from pathlib.PurePath documentation. """ self.assertEqual(self.path('foo//bar'), self.path('foo/bar')) self.assertEqual(self.path('foo/./bar'), self.path('foo/bar')) self.assertNotEqual(self.path('foo/../bar'), self.path('foo/bar')) self.assertEqual(self.path('/etc', '/usr', 'lib64'), self.path('/usr/lib64')) def test_path_parts(self): sep = self.os.path.sep path = self.path(sep + self.os.path.join('foo', 'bar', 'setup.py')) self.assertEqual(path.parts, (sep, 'foo', 'bar', 'setup.py')) self.assertEqual(path.drive, '') self.assertEqual(path.root, sep) self.assertEqual(path.anchor, sep) self.assertEqual(path.name, 'setup.py') self.assertEqual(path.stem, 'setup') self.assertEqual(path.suffix, '.py') self.assertEqual(path.parent, self.path(sep + self.os.path.join('foo', 'bar'))) self.assertEqual(path.parents[0], self.path(sep + self.os.path.join('foo', 'bar'))) self.assertEqual(path.parents[1], self.path(sep + 'foo')) self.assertEqual(path.parents[2], self.path(sep)) @unittest.skipIf(is_windows, 'POSIX specific behavior') def test_is_absolute_posix(self): self.assertTrue(self.path('/a/b').is_absolute()) self.assertFalse(self.path('a/b').is_absolute()) self.assertFalse(self.path('d:/b').is_absolute()) @unittest.skipIf(not is_windows, 'Windows specific behavior') def test_is_absolute_windows(self): self.assertFalse(self.path('/a/b').is_absolute()) self.assertFalse(self.path('a/b').is_absolute()) self.assertTrue(self.path('d:/b').is_absolute()) class RealPathlibInitializationTest(FakePathlibInitializationTest): def use_real_fs(self): return True @unittest.skipIf(not is_windows, 'Windows specific behavior') class FakePathlibInitializationWithDriveTest(RealPathlibTestCase): def test_init_with_segments(self): """Basic initialization tests - taken from pathlib.Path documentation""" self.assertEqual(self.path('c:/', 'foo', 'bar', 'baz'), self.path('c:/foo/bar/baz')) self.assertEqual(self.path(), self.path('.')) self.assertEqual(self.path(self.path('foo'), self.path('bar')), self.path('foo/bar')) self.assertEqual(self.path('c:/Users') / 'john' / 'data', self.path('c:/Users/john/data')) def test_init_collapse(self): """Tests for collapsing path during initialization. Taken from pathlib.PurePath documentation. """ self.assertEqual(self.path('c:/Windows', 'd:bar'), self.path('d:bar')) self.assertEqual(self.path('c:/Windows', '/Program Files'), self.path('c:/Program Files')) def test_path_parts(self): path = self.path( self.os.path.join('d:', 'python scripts', 'setup.py')) self.assertEqual(path.parts, ('d:', 'python scripts', 'setup.py')) self.assertEqual(path.drive, 'd:') self.assertEqual(path.root, '') self.assertEqual(path.anchor, 'd:') self.assertEqual(path.name, 'setup.py') self.assertEqual(path.stem, 'setup') self.assertEqual(path.suffix, '.py') self.assertEqual(path.parent, self.path( self.os.path.join('d:', 'python scripts'))) self.assertEqual(path.parents[0], self.path( self.os.path.join('d:', 'python scripts'))) self.assertEqual(path.parents[1], self.path('d:')) @unittest.skipIf(not is_windows, 'Windows-specifc behavior') def test_is_absolute(self): self.assertTrue(self.path('c:/a/b').is_absolute()) self.assertFalse(self.path('/a/b').is_absolute()) self.assertFalse(self.path('c:').is_absolute()) self.assertTrue(self.path('//some/share').is_absolute()) class RealPathlibInitializationWithDriveTest( FakePathlibInitializationWithDriveTest): def use_real_fs(self): return True class FakePathlibPurePathTest(RealPathlibTestCase): """Tests functionality present in PurePath class.""" @unittest.skipIf(is_windows, 'POSIX specific behavior') def test_is_reserved_posix(self): self.assertFalse(self.path('/dev').is_reserved()) self.assertFalse(self.path('/').is_reserved()) self.assertFalse(self.path('COM1').is_reserved()) self.assertFalse(self.path('nul.txt').is_reserved()) @unittest.skipIf(not is_windows, 'Windows specific behavior') def test_is_reserved_windows(self): self.check_windows_only() self.assertFalse(self.path('/dev').is_reserved()) self.assertFalse(self.path('/').is_reserved()) self.assertTrue(self.path('COM1').is_reserved()) self.assertTrue(self.path('nul.txt').is_reserved()) def test_joinpath(self): self.assertEqual(self.path('/etc').joinpath('passwd'), self.path('/etc/passwd')) self.assertEqual(self.path('/etc').joinpath(self.path('passwd')), self.path('/etc/passwd')) self.assertEqual(self.path('/foo').joinpath('bar', 'baz'), self.path('/foo/bar/baz')) def test_joinpath_drive(self): self.check_windows_only() self.assertEqual(self.path('c:').joinpath('/Program Files'), self.path('c:/Program Files')) def test_match(self): self.assertTrue(self.path('a/b.py').match('*.py')) self.assertTrue(self.path('/a/b/c.py').match('b/*.py')) self.assertFalse(self.path('/a/b/c.py').match('a/*.py')) self.assertTrue(self.path('/a.py').match('/*.py')) self.assertFalse(self.path('a/b.py').match('/*.py')) def test_relative_to(self): self.assertEqual(self.path('/etc/passwd').relative_to('/'), self.path('etc/passwd')) self.assertEqual(self.path('/etc/passwd').relative_to('/'), self.path('etc/passwd')) with self.assertRaises(ValueError): self.path('passwd').relative_to('/usr') @unittest.skipIf(sys.version_info < (3, 9), 'is_relative_to new in Python 3.9') def test_is_relative_to(self): path = self.path('/etc/passwd') self.assertTrue(path.is_relative_to('/etc')) self.assertFalse(path.is_relative_to('/src')) def test_with_name(self): self.check_windows_only() self.assertEqual( self.path('c:/Downloads/pathlib.tar.gz').with_name('setup.py'), self.path('c:/Downloads/setup.py')) with self.assertRaises(ValueError): self.path('c:/').with_name('setup.py') def test_with_suffix(self): self.assertEqual( self.path('c:/Downloads/pathlib.tar.gz').with_suffix('.bz2'), self.path('c:/Downloads/pathlib.tar.bz2')) self.assertEqual(self.path('README').with_suffix('.txt'), self.path('README.txt')) class RealPathlibPurePathTest(FakePathlibPurePathTest): def use_real_fs(self): return True class FakePathlibFileObjectPropertyTest(RealPathlibTestCase): def setUp(self): super(FakePathlibFileObjectPropertyTest, self).setUp() self.file_path = self.make_path('home', 'jane', 'test.py') self.create_file(self.file_path, contents=b'a' * 100) self.create_dir(self.make_path('home', 'john')) try: self.skip_if_symlink_not_supported() except unittest.SkipTest: return self.create_symlink(self.make_path('john'), self.make_path('home', 'john')) self.file_link_path = self.make_path('test.py') self.create_symlink(self.file_link_path, self.file_path) self.create_symlink(self.make_path('broken_dir_link'), self.make_path('home', 'none')) self.create_symlink(self.make_path('broken_file_link'), self.make_path('home', 'none', 'test.py')) def test_exists(self): self.skip_if_symlink_not_supported() self.assertTrue(self.path(self.file_path).exists()) self.assertTrue(self.path( self.make_path('home', 'jane')).exists()) self.assertFalse(self.path( self.make_path('home', 'jane', 'test')).exists()) self.assertTrue(self.path( self.make_path('john')).exists()) self.assertTrue(self.path( self.file_link_path).exists()) self.assertFalse(self.path( self.make_path('broken_dir_link')).exists()) self.assertFalse(self.path( self.make_path('broken_file_link')).exists()) def test_is_dir(self): self.skip_if_symlink_not_supported() self.assertFalse(self.path( self.file_path).is_dir()) self.assertTrue(self.path( self.make_path('home/jane')).is_dir()) self.assertTrue(self.path( self.make_path('john')).is_dir()) self.assertFalse(self.path( self.file_link_path).is_dir()) self.assertFalse(self.path( self.make_path('broken_dir_link')).is_dir()) self.assertFalse(self.path( self.make_path('broken_file_link')).is_dir()) def test_is_file(self): self.skip_if_symlink_not_supported() self.assertTrue(self.path( self.make_path('home/jane/test.py')).is_file()) self.assertFalse(self.path( self.make_path('home/jane')).is_file()) self.assertFalse(self.path( self.make_path('john')).is_file()) self.assertTrue(self.path( self.file_link_path).is_file()) self.assertFalse(self.path( self.make_path('broken_dir_link')).is_file()) self.assertFalse(self.path( self.make_path('broken_file_link')).is_file()) def test_is_symlink(self): self.skip_if_symlink_not_supported() self.assertFalse(self.path( self.make_path('home/jane/test.py')).is_symlink()) self.assertFalse(self.path( self.make_path('home/jane')).is_symlink()) self.assertTrue(self.path( self.make_path('john')).is_symlink()) self.assertTrue(self.path( self.file_link_path).is_symlink()) self.assertTrue(self.path( self.make_path('broken_dir_link')).is_symlink()) self.assertTrue(self.path( self.make_path('broken_file_link')).is_symlink()) def test_stat(self): self.skip_if_symlink_not_supported() file_stat = self.os.stat(self.file_path) stat_result = self.path(self.file_link_path).stat() self.assertFalse(stat_result.st_mode & stat.S_IFDIR) self.assertTrue(stat_result.st_mode & stat.S_IFREG) self.assertEqual(stat_result.st_ino, file_stat.st_ino) self.assertEqual(stat_result.st_size, 100) self.assertEqual(stat_result.st_mtime, file_stat.st_mtime) self.assertEqual(stat_result[stat.ST_MTIME], int(file_stat.st_mtime)) def check_lstat(self, expected_size): self.skip_if_symlink_not_supported() link_stat = self.os.lstat(self.file_link_path) stat_result = self.path(self.file_link_path).lstat() self.assertTrue(stat_result.st_mode & stat.S_IFREG) self.assertTrue(stat_result.st_mode & stat.S_IFLNK) self.assertEqual(stat_result.st_ino, link_stat.st_ino) self.assertEqual(stat_result.st_size, expected_size) self.assertEqual(stat_result.st_mtime, link_stat.st_mtime) @unittest.skipIf(is_windows, 'POSIX specific behavior') def test_lstat_posix(self): self.check_lstat(len(self.file_path)) @unittest.skipIf(not is_windows, 'Windows specific behavior') def test_lstat_windows(self): self.skip_if_symlink_not_supported() self.check_lstat(0) @unittest.skipIf(is_windows, 'Linux specific behavior') def test_chmod(self): self.check_linux_only() file_stat = self.os.stat(self.file_path) self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o666) link_stat = self.os.lstat(self.file_link_path) # we get stat.S_IFLNK | 0o755 under MacOs self.assertEqual(link_stat.st_mode, stat.S_IFLNK | 0o777) def test_lchmod(self): self.skip_if_symlink_not_supported() file_stat = self.os.stat(self.file_path) link_stat = self.os.lstat(self.file_link_path) if not hasattr(os, "lchmod"): with self.assertRaises(NotImplementedError): self.path(self.file_link_path).lchmod(0o444) else: self.path(self.file_link_path).lchmod(0o444) self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o666) # the exact mode depends on OS and Python version self.assertEqual(link_stat.st_mode & 0o777700, stat.S_IFLNK | 0o700) @unittest.skipIf(sys.version_info < (3, 10), "follow_symlinks argument new in Python 3.10") def test_chmod_no_followsymlinks(self): self.skip_if_symlink_not_supported() file_stat = self.os.stat(self.file_path) link_stat = self.os.lstat(self.file_link_path) if os.chmod not in os.supports_follow_symlinks or IS_PYPY: with self.assertRaises(NotImplementedError): self.path(self.file_link_path).chmod(0o444, follow_symlinks=False) else: self.path(self.file_link_path).chmod(0o444, follow_symlinks=False) self.assertEqual(file_stat.st_mode, stat.S_IFREG | 0o666) # the exact mode depends on OS and Python version self.assertEqual(link_stat.st_mode & 0o777700, stat.S_IFLNK | 0o700) def test_resolve(self): self.create_dir(self.make_path('antoine', 'docs')) self.create_file(self.make_path('antoine', 'setup.py')) self.os.chdir(self.make_path('antoine')) # use real path to handle symlink /var to /private/var in MacOs self.assert_equal_paths(self.path().resolve(), self.path(self.os.path.realpath( self.make_path('antoine')))) self.assert_equal_paths( self.path( self.os.path.join('docs', '..', 'setup.py')).resolve(), self.path( self.os.path.realpath( self.make_path('antoine', 'setup.py')))) def test_stat_file_in_unreadable_dir(self): self.check_posix_only() dir_path = self.make_path('some_dir') file_path = self.os.path.join(dir_path, 'some_file') self.create_file(file_path) self.os.chmod(dir_path, 0o000) if not is_root(): self.assert_raises_os_error( errno.EACCES, self.path(file_path).stat) else: self.assertEqual(0, self.path(file_path).stat().st_size) def test_iterdir_in_unreadable_dir(self): self.check_posix_only() dir_path = self.make_path('some_dir') file_path = self.os.path.join(dir_path, 'some_file') self.create_file(file_path) self.os.chmod(dir_path, 0o000) iter = self.path(dir_path).iterdir() if not is_root(): self.assert_raises_os_error(errno.EACCES, list, iter) else: path = str(list(iter)[0]) self.assertTrue(path.endswith('some_file')) def test_resolve_nonexisting_file(self): path = self.path( self.make_path('/path', 'to', 'file', 'this can not exist')) self.assertEqual(path, path.resolve()) def test_cwd(self): dir_path = self.make_path('jane') self.create_dir(dir_path) self.os.chdir(dir_path) self.assert_equal_paths(self.path.cwd(), self.path(self.os.path.realpath(dir_path))) def test_expanduser(self): if is_windows: self.assertEqual(self.path('~').expanduser(), self.path( os.environ['USERPROFILE'].replace('\\', '/'))) else: self.assertEqual(self.path('~').expanduser(), self.path(os.environ['HOME'])) def test_home(self): if is_windows: self.assertEqual(self.path( os.environ['USERPROFILE'].replace('\\', '/')), self.path.home()) else: self.assertEqual(self.path(os.environ['HOME']), self.path.home()) class RealPathlibFileObjectPropertyTest(FakePathlibFileObjectPropertyTest): def use_real_fs(self): return True class FakePathlibPathFileOperationTest(RealPathlibTestCase): """Tests methods related to file and directory handling.""" def test_exists(self): self.skip_if_symlink_not_supported() self.create_file(self.make_path('home', 'jane', 'test.py')) self.create_dir(self.make_path('home', 'john')) self.create_symlink( self.make_path('john'), self.make_path('home', 'john')) self.create_symlink( self.make_path('none'), self.make_path('home', 'none')) self.assertTrue( self.path(self.make_path('home', 'jane', 'test.py')).exists()) self.assertTrue(self.path(self.make_path('home', 'jane')).exists()) self.assertTrue(self.path(self.make_path('john')).exists()) self.assertFalse(self.path(self.make_path('none')).exists()) self.assertFalse( self.path(self.make_path('home', 'jane', 'test')).exists()) def test_open(self): self.create_dir(self.make_path('foo')) with self.assertRaises(OSError): self.path(self.make_path('foo', 'bar.txt')).open() self.path(self.make_path('foo', 'bar.txt')).open('w').close() self.assertTrue( self.os.path.exists(self.make_path('foo', 'bar.txt'))) def test_read_text(self): self.create_file(self.make_path('text_file'), contents='foo') file_path = self.path(self.make_path('text_file')) self.assertEqual(file_path.read_text(), 'foo') def test_read_text_with_encoding(self): self.create_file(self.make_path('text_file'), contents='ерунда', encoding='cyrillic') file_path = self.path(self.make_path('text_file')) self.assertEqual(file_path.read_text(encoding='cyrillic'), 'ерунда') def test_write_text(self): path_name = self.make_path('text_file') file_path = self.path(path_name) file_path.write_text(str('foo')) self.assertTrue(self.os.path.exists(path_name)) self.check_contents(path_name, 'foo') def test_write_text_with_encoding(self): path_name = self.make_path('text_file') file_path = self.path(path_name) file_path.write_text('ανοησίες', encoding='greek') self.assertTrue(self.os.path.exists(path_name)) self.check_contents(path_name, 'ανοησίες'.encode('greek')) @unittest.skipIf(sys.version_info < (3, 10), "newline argument new in Python 3.10") def test_write_with_newline_arg(self): path = self.path(self.make_path('some_file')) path.write_text('1\r\n2\n3\r4', newline='') self.check_contents(path, b'1\r\n2\n3\r4') path.write_text('1\r\n2\n3\r4', newline='\n') self.check_contents(path, b'1\r\n2\n3\r4') path.write_text('1\r\n2\n3\r4', newline='\r\n') self.check_contents(path, b'1\r\r\n2\r\n3\r4') path.write_text('1\r\n2\n3\r4', newline='\r') self.check_contents(path, b'1\r\r2\r3\r4') def test_read_bytes(self): path_name = self.make_path('binary_file') self.create_file(path_name, contents=b'Binary file contents') file_path = self.path(path_name) self.assertEqual(file_path.read_bytes(), b'Binary file contents') def test_write_bytes(self): path_name = self.make_path('binary_file') file_path = self.path(path_name) file_path.write_bytes(b'Binary file contents') self.assertTrue(self.os.path.exists(path_name)) self.check_contents(path_name, b'Binary file contents') def test_rename(self): file_name = self.make_path('foo', 'bar.txt') self.create_file(file_name, contents='test') new_file_name = self.make_path('foo', 'baz.txt') self.path(file_name).rename(new_file_name) self.assertFalse(self.os.path.exists(file_name)) self.check_contents(new_file_name, 'test') def test_replace(self): self.create_file(self.make_path('foo', 'bar.txt'), contents='test') self.create_file(self.make_path('bar', 'old.txt'), contents='replaced') self.path(self.make_path('bar', 'old.txt')).replace( self.make_path('foo', 'bar.txt')) self.assertFalse( self.os.path.exists(self.make_path('bar', 'old.txt'))) self.check_contents(self.make_path('foo', 'bar.txt'), 'replaced') def test_unlink(self): file_path = self.make_path('foo', 'bar.txt') self.create_file(file_path, contents='test') self.assertTrue(self.os.path.exists(file_path)) self.path(file_path).unlink() self.assertFalse(self.os.path.exists(file_path)) def test_touch_non_existing(self): self.create_dir(self.make_path('foo')) file_name = self.make_path('foo', 'bar.txt') self.path(file_name).touch(mode=0o444) self.check_contents(file_name, '') self.assertTrue(self.os.stat(file_name).st_mode, stat.S_IFREG | 0o444) self.os.chmod(file_name, mode=0o666) def test_touch_existing(self): file_name = self.make_path('foo', 'bar.txt') self.create_file(file_name, contents='test') file_path = self.path(file_name) self.assert_raises_os_error( errno.EEXIST, file_path.touch, exist_ok=False) file_path.touch() self.check_contents(file_name, 'test') def test_samefile(self): file_name = self.make_path('foo', 'bar.txt') self.create_file(file_name) file_name2 = self.make_path('foo', 'baz.txt') self.create_file(file_name2) with self.assertRaises(OSError): self.path(self.make_path('foo', 'other')).samefile( self.make_path('foo', 'other.txt')) path = self.path(file_name) other_name = self.make_path('foo', 'other.txt') with self.assertRaises(OSError): path.samefile(other_name) with self.assertRaises(OSError): path.samefile(self.path(other_name)) self.assertFalse(path.samefile(file_name2)) self.assertFalse(path.samefile(self.path(file_name2))) self.assertTrue( path.samefile(self.make_path('foo', '..', 'foo', 'bar.txt'))) self.assertTrue(path.samefile( self.path(self.make_path('foo', '..', 'foo', 'bar.txt')))) def test_symlink_to(self): self.skip_if_symlink_not_supported() file_name = self.make_path('foo', 'bar.txt') self.create_file(file_name) link_name = self.make_path('link_to_bar') path = self.path(link_name) path.symlink_to(file_name) self.assertTrue(self.os.path.exists(link_name)) self.assertTrue(path.is_symlink()) @unittest.skipIf(sys.version_info < (3, 8), 'link_to new in Python 3.8') def test_link_to(self): self.skip_if_symlink_not_supported() file_name = self.make_path('foo', 'bar.txt') self.create_file(file_name) self.assertEqual(1, self.os.stat(file_name).st_nlink) link_name = self.make_path('link_to_bar') path = self.path(file_name) path.link_to(link_name) self.assertTrue(self.os.path.exists(link_name)) self.assertFalse(path.is_symlink()) self.assertEqual(2, self.os.stat(file_name).st_nlink) @unittest.skipIf(sys.version_info < (3, 10), 'hardlink_to new in Python 3.10') def test_hardlink_to(self): self.skip_if_symlink_not_supported() file_name = self.make_path('foo', 'bar.txt') self.create_file(file_name) self.assertEqual(1, self.os.stat(file_name).st_nlink) link_path = self.path(self.make_path('link_to_bar')) path = self.path(file_name) link_path.hardlink_to(path) self.assertTrue(self.os.path.exists(link_path)) self.assertFalse(path.is_symlink()) self.assertEqual(2, self.os.stat(file_name).st_nlink) @unittest.skipIf(sys.version_info < (3, 9), 'readlink new in Python 3.9') def test_readlink(self): self.skip_if_symlink_not_supported() link_path = self.make_path('foo', 'bar', 'baz') target = self.make_path('tarJAY') self.create_symlink(link_path, target) path = self.path(link_path) self.assert_equal_paths(path.readlink(), self.path(target)) def test_mkdir(self): dir_name = self.make_path('foo', 'bar') self.assert_raises_os_error(errno.ENOENT, self.path(dir_name).mkdir) self.path(dir_name).mkdir(parents=True) self.assertTrue(self.os.path.exists(dir_name)) self.assert_raises_os_error(errno.EEXIST, self.path(dir_name).mkdir) def test_mkdir_exist_ok(self): dir_name = self.make_path('foo', 'bar') self.create_dir(dir_name) self.path(dir_name).mkdir(exist_ok=True) file_name = self.os.path.join(dir_name, 'baz') self.create_file(file_name) self.assert_raises_os_error(errno.EEXIST, self.path(file_name).mkdir, exist_ok=True) def test_rmdir(self): dir_name = self.make_path('foo', 'bar') self.create_dir(dir_name) self.path(dir_name).rmdir() self.assertFalse(self.os.path.exists(dir_name)) self.assertTrue(self.os.path.exists(self.make_path('foo'))) self.create_file(self.make_path('foo', 'baz')) with self.assertRaises(OSError): self.path(self.make_path('foo')).rmdir() self.assertTrue(self.os.path.exists(self.make_path('foo'))) def test_iterdir(self): self.create_file(self.make_path('foo', 'bar', 'file1')) self.create_file(self.make_path('foo', 'bar', 'file2')) self.create_file(self.make_path('foo', 'bar', 'file3')) path = self.path(self.make_path('foo', 'bar')) contents = [entry for entry in path.iterdir()] self.assertEqual(3, len(contents)) self.assertIn(self.path(self.make_path('foo', 'bar', 'file2')), contents) def test_glob(self): self.create_file(self.make_path('foo', 'setup.py')) self.create_file(self.make_path('foo', 'all_tests.py')) self.create_file(self.make_path('foo', 'README.md')) self.create_file(self.make_path('foo', 'setup.pyc')) path = self.path(self.make_path('foo')) self.assertEqual(sorted(path.glob('*.py')), [self.path(self.make_path('foo', 'all_tests.py')), self.path(self.make_path('foo', 'setup.py'))]) @unittest.skipIf(not is_windows, 'Windows specific test') def test_glob_case_windows(self): self.create_file(self.make_path('foo', 'setup.py')) self.create_file(self.make_path('foo', 'all_tests.PY')) self.create_file(self.make_path('foo', 'README.md')) self.create_file(self.make_path('foo', 'example.Py')) path = self.path(self.make_path('foo')) self.assertEqual(sorted(path.glob('*.py')), [self.path(self.make_path('foo', 'all_tests.PY')), self.path(self.make_path('foo', 'example.Py')), self.path(self.make_path('foo', 'setup.py'))]) @unittest.skipIf(is_windows, 'Posix specific test') def test_glob_case_posix(self): self.check_posix_only() self.create_file(self.make_path('foo', 'setup.py')) self.create_file(self.make_path('foo', 'all_tests.PY')) self.create_file(self.make_path('foo', 'README.md')) self.create_file(self.make_path('foo', 'example.Py')) path = self.path(self.make_path('foo')) self.assertEqual(sorted(path.glob('*.py')), [self.path(self.make_path('foo', 'setup.py'))]) class RealPathlibPathFileOperationTest(FakePathlibPathFileOperationTest): def use_real_fs(self): return True @unittest.skipIf(sys.version_info < (3, 6), 'path-like objects new in Python 3.6') class FakePathlibUsageInOsFunctionsTest(RealPathlibTestCase): """Test that many os / os.path functions accept a path-like object since Python 3.6. The functionality of these functions is tested elsewhere, we just check that they accept a fake path object as an argument. """ def test_join(self): dir1 = 'foo' dir2 = 'bar' dir = self.os.path.join(dir1, dir2) self.assertEqual(dir, self.os.path.join(self.path(dir1), dir2)) self.assertEqual(dir, self.os.path.join(dir1, self.path(dir2))) self.assertEqual(dir, self.os.path.join(self.path(dir1), self.path(dir2))) def test_normcase(self): dir1 = self.make_path('Foo', 'Bar', 'Baz') self.assertEqual(self.os.path.normcase(dir1), self.os.path.normcase(self.path(dir1))) def test_normpath(self): dir1 = self.make_path('foo', 'bar', '..', 'baz') self.assertEqual(self.os.path.normpath(dir1), self.os.path.normpath(self.path(dir1))) def test_realpath(self): dir1 = self.make_path('foo', 'bar', '..', 'baz') self.assertEqual(self.os.path.realpath(dir1), self.os.path.realpath(self.path(dir1))) def test_relpath(self): path_foo = self.make_path('path', 'to', 'foo') path_bar = self.make_path('path', 'to', 'bar') rel_path = self.os.path.relpath(path_foo, path_bar) self.assertEqual(rel_path, self.os.path.relpath(self.path(path_foo), path_bar)) self.assertEqual(rel_path, self.os.path.relpath(path_foo, self.path(path_bar))) self.assertEqual(rel_path, self.os.path.relpath(self.path(path_foo), self.path(path_bar))) def test_split(self): dir1 = self.make_path('Foo', 'Bar', 'Baz') self.assertEqual(self.os.path.split(dir1), self.os.path.split(self.path(dir1))) def test_splitdrive(self): dir1 = self.make_path('C:', 'Foo', 'Bar', 'Baz') self.assertEqual(self.os.path.splitdrive(dir1), self.os.path.splitdrive(self.path(dir1))) def test_abspath(self): dir1 = self.make_path('foo', 'bar', '..', 'baz') self.assertEqual(self.os.path.abspath(dir1), self.os.path.abspath(self.path(dir1))) def test_exists(self): dir1 = self.make_path('foo', 'bar', '..', 'baz') self.assertEqual(self.os.path.exists(dir1), self.os.path.exists(self.path(dir1))) def test_lexists(self): dir1 = self.make_path('foo', 'bar', '..', 'baz') self.assertEqual(self.os.path.lexists(dir1), self.os.path.lexists(self.path(dir1))) def test_expanduser(self): dir1 = self.os.path.join('~', 'foo') self.assertEqual(self.os.path.expanduser(dir1), self.os.path.expanduser(self.path(dir1))) def test_getmtime(self): self.skip_real_fs() dir1 = self.make_path('foo', 'bar1.txt') path_obj = self.filesystem.create_file(dir1) path_obj._st_mtime = 24 self.assertEqual(self.os.path.getmtime(dir1), self.os.path.getmtime(self.path(dir1))) def test_getctime(self): self.skip_real_fs() dir1 = self.make_path('foo', 'bar1.txt') path_obj = self.filesystem.create_file(dir1) path_obj.st_ctime = 42 self.assertEqual(self.os.path.getctime(dir1), self.os.path.getctime(self.path(dir1))) def test_getatime(self): self.skip_real_fs() dir1 = self.make_path('foo', 'bar1.txt') path_obj = self.filesystem.create_file(dir1) path_obj.st_atime = 11 self.assertEqual(self.os.path.getatime(dir1), self.os.path.getatime(self.path(dir1))) def test_getsize(self): path = self.make_path('foo', 'bar', 'baz') self.create_file(path, contents='1234567') self.assertEqual(self.os.path.getsize(path), self.os.path.getsize(self.path(path))) def test_isabs(self): path = self.make_path('foo', 'bar', '..', 'baz') self.assertEqual(self.os.path.isabs(path), self.os.path.isabs(self.path(path))) def test_isfile(self): path = self.make_path('foo', 'bar', 'baz') self.create_file(path) self.assertEqual(self.os.path.isfile(path), self.os.path.isfile(self.path(path))) def test_isfile_not_readable(self): path = self.make_path('foo', 'bar', 'baz') self.create_file(path, perm=0) self.assertEqual(self.os.path.isfile(path), self.os.path.isfile(self.path(path))) def test_islink(self): path = self.make_path('foo', 'bar', 'baz') self.create_file(path) self.assertEqual(self.os.path.islink(path), self.os.path.islink(self.path(path))) def test_isdir(self): path = self.make_path('foo', 'bar', 'baz') self.create_file(path) self.assertEqual(self.os.path.isdir(path), self.os.path.isdir(self.path(path))) def test_ismount(self): path = self.os.path.sep self.assertEqual(self.os.path.ismount(path), self.os.path.ismount(self.path(path))) def test_access(self): path = self.make_path('foo', 'bar', 'baz') self.create_file(path, contents='1234567') self.assertEqual(self.os.access(path, os.R_OK), self.os.access(self.path(path), os.R_OK)) def test_chdir(self): path = self.make_path('foo', 'bar', 'baz') self.create_dir(path) self.os.chdir(self.path(path)) # use real path to handle symlink /var to /private/var in MacOs self.assert_equal_paths(self.os.path.realpath(path), self.os.getcwd()) def test_chmod(self): path = self.make_path('some_file') self.create_file(path) self.os.chmod(self.path(path), 0o444) self.assertEqual(stat.S_IMODE(0o444), stat.S_IMODE(self.os.stat(path).st_mode)) self.os.chmod(self.path(path), 0o666) def test_link(self): self.skip_if_symlink_not_supported() file1_path = self.make_path('test_file1') file2_path = self.make_path('test_file2') self.create_file(file1_path) self.os.link(self.path(file1_path), file2_path) self.assertTrue(self.os.path.exists(file2_path)) self.os.unlink(file2_path) self.os.link(self.path(file1_path), self.path(file2_path)) self.assertTrue(self.os.path.exists(file2_path)) self.os.unlink(file2_path) self.os.link(file1_path, self.path(file2_path)) self.assertTrue(self.os.path.exists(file2_path)) def test_listdir(self): path = self.make_path('foo', 'bar') self.create_dir(path) self.create_file(path + 'baz.txt') self.assertEqual(self.os.listdir(path), self.os.listdir(self.path(path))) def test_mkdir(self): path = self.make_path('foo') self.os.mkdir(self.path(path)) self.assertTrue(self.os.path.exists(path)) def test_makedirs(self): path = self.make_path('foo', 'bar') self.os.makedirs(self.path(path)) self.assertTrue(self.os.path.exists(path)) @unittest.skipIf(is_windows and sys.version_info < (3, 8), 'os.readlink does not to support path-like objects ' 'under Windows before Python 3.8') def test_readlink(self): self.skip_if_symlink_not_supported() link_path = self.make_path('foo', 'bar', 'baz') target = self.make_path('tarJAY') self.create_symlink(link_path, target) self.assert_equal_paths(self.os.readlink(self.path(link_path)), target) @unittest.skipIf(is_windows and sys.version_info < (3, 8), 'os.readlink does not to support path-like objects ' 'under Windows before Python 3.8') def test_readlink_bytes(self): self.skip_if_symlink_not_supported() link_path = self.make_path(b'foo', b'bar', b'baz') target = self.make_path(b'tarJAY') self.create_symlink(link_path, target) self.assert_equal_paths(self.os.readlink(self.path(link_path)), target) def test_remove(self): path = self.make_path('test.txt') self.create_file(path) self.os.remove(self.path(path)) self.assertFalse(self.os.path.exists(path)) def test_rename(self): path1 = self.make_path('test1.txt') path2 = self.make_path('test2.txt') self.create_file(path1) self.os.rename(self.path(path1), path2) self.assertTrue(self.os.path.exists(path2)) self.os.rename(self.path(path2), self.path(path1)) self.assertTrue(self.os.path.exists(path1)) def test_replace(self): path1 = self.make_path('test1.txt') path2 = self.make_path('test2.txt') self.create_file(path1) self.os.replace(self.path(path1), path2) self.assertTrue(self.os.path.exists(path2)) self.os.replace(self.path(path2), self.path(path1)) self.assertTrue(self.os.path.exists(path1)) def test_rmdir(self): path = self.make_path('foo', 'bar') self.create_dir(path) self.os.rmdir(self.path(path)) self.assertFalse(self.os.path.exists(path)) def test_scandir(self): directory = self.make_path('xyzzy', 'plugh') self.create_dir(directory) self.create_file(self.os.path.join(directory, 'test.txt')) dir_entries = [entry for entry in self.os.scandir(self.path(directory))] self.assertEqual(1, len(dir_entries)) def test_symlink(self): self.skip_if_symlink_not_supported() file_path = self.make_path('test_file1') link_path = self.make_path('link') self.create_file(file_path) self.os.symlink(self.path(file_path), link_path) self.assertTrue(self.os.path.exists(link_path)) self.os.remove(link_path) self.os.symlink(self.path(file_path), self.path(link_path)) self.assertTrue(self.os.path.exists(link_path)) def test_stat(self): path = self.make_path('foo', 'bar', 'baz') self.create_file(path, contents='1234567') self.assertEqual(self.os.stat(path), self.path(path).stat()) @unittest.skipIf(sys.version_info < (3, 10), "New in Python 3.10") def test_stat_follow_symlinks(self): self.check_posix_only() directory = self.make_path('foo') base_name = 'bar' file_path = self.path(self.os.path.join(directory, base_name)) link_path = self.path(self.os.path.join(directory, 'link')) contents = "contents" self.create_file(file_path, contents=contents) self.create_symlink(link_path, base_name) self.assertEqual(len(contents), link_path.stat(follow_symlinks=True)[stat.ST_SIZE]) self.assertEqual(len(base_name), link_path.stat(follow_symlinks=False)[stat.ST_SIZE]) def test_utime(self): path = self.make_path('some_file') self.create_file(path, contents='test') self.os.utime(self.path(path), times=(1, 2)) st = self.os.stat(path) self.assertEqual(1, st.st_atime) self.assertEqual(2, st.st_mtime) def test_truncate(self): path = self.make_path('some_file') self.create_file(path, contents='test_test') self.os.truncate(self.path(path), length=4) st = self.os.stat(path) self.assertEqual(4, st.st_size) @unittest.skipIf(sys.platform == 'win32', 'no pwd and grp modules in Windows') def test_owner_and_group_posix(self): self.check_posix_only() path = self.make_path('some_file') self.create_file(path) self.assertTrue(self.path(path).owner()) self.assertTrue(self.path(path).group()) def test_owner_and_group_windows(self): self.check_windows_only() path = self.make_path('some_file') self.create_file(path) with self.assertRaises(NotImplementedError): self.path(path).owner() with self.assertRaises(NotImplementedError): self.path(path).group() class RealPathlibUsageInOsFunctionsTest(FakePathlibUsageInOsFunctionsTest): def use_real_fs(self): return True @unittest.skipIf(sys.version_info < (3, 6), 'Path-like objects new in Python 3.6') class FakeFilesystemPathLikeObjectTest(unittest.TestCase): def setUp(self): self.filesystem = fake_filesystem.FakeFilesystem( path_separator='/') self.pathlib = fake_pathlib.FakePathlibModule(self.filesystem) self.os = fake_filesystem.FakeOsModule(self.filesystem) def test_create_dir_with_pathlib_path(self): dir_path_string = 'foo/bar/baz' dir_path = self.pathlib.Path(dir_path_string) self.filesystem.create_dir(dir_path) self.assertTrue(self.os.path.exists(dir_path_string)) self.assertEqual(stat.S_IFDIR, self.os.stat( dir_path_string).st_mode & stat.S_IFDIR) def test_create_file_with_pathlib_path(self): file_path_string = 'foo/bar/baz' file_path = self.pathlib.Path(file_path_string) self.filesystem.create_file(file_path) self.assertTrue(self.os.path.exists(file_path_string)) self.assertEqual(stat.S_IFREG, self.os.stat( file_path_string).st_mode & stat.S_IFREG) def test_create_symlink_with_pathlib_path(self): file_path = self.pathlib.Path('foo/bar/baz') link_path_string = 'foo/link' link_path = self.pathlib.Path(link_path_string) self.filesystem.create_symlink(link_path, file_path) self.assertTrue(self.os.path.lexists(link_path_string)) self.assertEqual(stat.S_IFLNK, self.os.lstat(link_path_string).st_mode & stat.S_IFLNK) def test_add_existing_real_file_with_pathlib_path(self): real_file_path_string = os.path.abspath(__file__) real_file_path = self.pathlib.Path(real_file_path_string) self.filesystem.add_real_file(real_file_path) fake_filepath_string = real_file_path_string.replace( os.sep, self.os.sep) self.assertTrue(self.os.path.exists(fake_filepath_string)) self.assertEqual(stat.S_IFREG, self.os.stat( fake_filepath_string).st_mode & stat.S_IFREG) def test_add_existing_real_directory_with_pathlib_path(self): real_dirpath_string = os.path.dirname(os.path.abspath(__file__)) real_dir_path = self.pathlib.Path(real_dirpath_string) self.filesystem.add_real_directory(real_dir_path) fake_dirpath_string = real_dirpath_string.replace( os.sep, self.os.sep) self.assertTrue(self.os.path.exists(fake_dirpath_string)) self.assertEqual(stat.S_IFDIR, self.os.stat( fake_dirpath_string).st_mode & stat.S_IFDIR) if __name__ == '__main__': unittest.main(verbosity=2) pyfakefs-4.5.4/pyfakefs/tests/fake_stat_time_test.py0000666000000000000000000005460414147521400021037 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for file timestamp updates.""" import time import unittest from collections import namedtuple from pyfakefs.tests.test_utils import RealFsTestCase FileTime = namedtuple('FileTime', 'st_ctime, st_atime, st_mtime') class FakeStatTestBase(RealFsTestCase): def setUp(self): super().setUp() # we disable the tests for MacOS to avoid very long builds due # to the 1s time resolution - we know that the functionality is # similar to Linux self.check_linux_and_windows() self.file_path = self.make_path('some_file') # MacOS has a timestamp resolution of 1 second self.sleep_time = 1.1 if self.is_macos else 0.01 self.mode = '' def stat_time(self, path): stat = self.os.stat(path) if self.use_real_fs(): # sleep a bit so in the next call the time has changed time.sleep(self.sleep_time) else: # calling time.time() advances mocked time time.time() return FileTime(st_ctime=stat.st_ctime, st_atime=stat.st_atime, st_mtime=stat.st_mtime) def assertLessExceptWindows(self, time1, time2): if self.is_windows_fs: self.assertLessEqual(time1, time2) else: self.assertLess(time1, time2) def assertLessExceptPosix(self, time1, time2): if self.is_windows_fs: self.assertLess(time1, time2) else: self.assertEqual(time1, time2) def open_close_new_file(self): with self.mock_time(): with self.open(self.file_path, self.mode): created = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return created, closed def open_write_close_new_file(self): with self.mock_time(): with self.open(self.file_path, self.mode) as f: created = self.stat_time(self.file_path) f.write('foo') written = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return created, written, closed def open_close(self): with self.mock_time(): self.create_file(self.file_path) before = self.stat_time(self.file_path) with self.open(self.file_path, self.mode): opened = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return before, opened, closed def open_write_close(self): with self.mock_time(): self.create_file(self.file_path) before = self.stat_time(self.file_path) with self.open(self.file_path, self.mode) as f: opened = self.stat_time(self.file_path) f.write('foo') written = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return before, opened, written, closed def open_flush_close(self): with self.mock_time(): self.create_file(self.file_path) before = self.stat_time(self.file_path) with self.open(self.file_path, self.mode) as f: opened = self.stat_time(self.file_path) f.flush() flushed = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return before, opened, flushed, closed def open_write_flush(self): with self.mock_time(): self.create_file(self.file_path) before = self.stat_time(self.file_path) with self.open(self.file_path, self.mode) as f: opened = self.stat_time(self.file_path) f.write('foo') written = self.stat_time(self.file_path) f.flush() flushed = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return before, opened, written, flushed, closed def open_read_flush(self): with self.mock_time(): self.create_file(self.file_path) before = self.stat_time(self.file_path) with self.open(self.file_path, 'r') as f: opened = self.stat_time(self.file_path) f.read() read = self.stat_time(self.file_path) f.flush() flushed = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return before, opened, read, flushed, closed def open_read_close_new_file(self): with self.mock_time(): with self.open(self.file_path, self.mode) as f: created = self.stat_time(self.file_path) f.read() read = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return created, read, closed def open_read_close(self): with self.mock_time(): self.create_file(self.file_path) before = self.stat_time(self.file_path) with self.open(self.file_path, self.mode) as f: opened = self.stat_time(self.file_path) f.read() read = self.stat_time(self.file_path) closed = self.stat_time(self.file_path) return before, opened, read, closed def check_open_close_new_file(self): """ When a file is created on opening and closed again, no timestamps are updated on close. """ created, closed = self.open_close_new_file() self.assertEqual(created.st_ctime, closed.st_ctime) self.assertEqual(created.st_atime, closed.st_atime) self.assertEqual(created.st_mtime, closed.st_mtime) def check_open_write_close_new_file(self): """ When a file is created on opening, st_ctime is updated under Posix, and st_mtime is updated on close. """ created, written, closed = self.open_write_close_new_file() self.assertEqual(created.st_ctime, written.st_ctime) self.assertLessExceptWindows(written.st_ctime, closed.st_ctime) self.assertEqual(created.st_atime, written.st_atime) self.assertLessEqual(written.st_atime, closed.st_atime) self.assertEqual(created.st_mtime, written.st_mtime) self.assertLess(written.st_mtime, closed.st_mtime) def check_open_close_w_mode(self): """ When an existing file is opened with 'w' or 'w+' mode, st_ctime (Posix only) and st_mtime are updated on open (truncating), but not on close. """ before, opened, closed = self.open_close() self.assertLessExceptWindows(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, closed.st_ctime) self.assertLessEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, closed.st_atime) self.assertLess(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, closed.st_mtime) def check_open_close_non_w_mode(self): """ When an existing file is opened with any mode other than 'w' or 'w+', no timestamps are updated. """ before, opened, closed = self.open_close() self.assertEqual(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, closed.st_ctime) self.assertEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, closed.st_atime) self.assertEqual(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, closed.st_mtime) def check_open_write_close_w_mode(self): """ When an existing file is opened with 'w' or 'w+' mode and is then written to, st_ctime (Posix only) and st_mtime are updated on open (truncating) and again on close (flush), but not when written to. """ before, opened, written, closed = self.open_write_close() self.assertLessExceptWindows(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, written.st_ctime) self.assertLessExceptWindows(written.st_ctime, closed.st_ctime) self.assertLessEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, written.st_atime) self.assertLessEqual(written.st_atime, closed.st_atime) self.assertLess(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, written.st_mtime) self.assertLess(written.st_mtime, closed.st_mtime) def check_open_flush_close_w_mode(self): """ When an existing file is opened with 'w' or 'w+' mode (truncating), st_ctime (Posix only) and st_mtime are updated. No updates are done on flush or close. """ before, opened, flushed, closed = self.open_flush_close() self.assertLessExceptWindows(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, flushed.st_ctime) self.assertEqual(flushed.st_ctime, closed.st_ctime) self.assertLessEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, flushed.st_atime) self.assertEqual(flushed.st_atime, closed.st_atime) self.assertLess(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, flushed.st_mtime) self.assertEqual(flushed.st_mtime, closed.st_mtime) def check_open_flush_close_non_w_mode(self): """ When an existing file is opened with any mode other than 'w' or 'w+', flushed and closed, no timestamps are updated. """ before, opened, flushed, closed = self.open_flush_close() self.assertEqual(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, flushed.st_ctime) self.assertEqual(flushed.st_ctime, closed.st_ctime) self.assertEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, flushed.st_atime) self.assertEqual(flushed.st_atime, closed.st_atime) self.assertEqual(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, flushed.st_mtime) self.assertEqual(flushed.st_mtime, closed.st_mtime) def check_open_read_close_non_w_mode(self): """ Reading from a file opened with 'r', 'r+', or 'a+' mode updates st_atime under Posix. """ before, opened, read, closed = self.open_read_close() self.assertEqual(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, read.st_ctime) self.assertEqual(read.st_ctime, closed.st_ctime) self.assertEqual(before.st_atime, opened.st_atime) self.assertLessEqual(opened.st_atime, read.st_atime) self.assertEqual(read.st_atime, closed.st_atime) self.assertEqual(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, read.st_mtime) self.assertEqual(read.st_mtime, closed.st_mtime) def check_open_read_close_new_file(self): """ When a file is created with 'w+' or 'a+' mode and then read from, st_atime is updated under Posix. """ created, read, closed = self.open_read_close_new_file() self.assertEqual(created.st_ctime, read.st_ctime) self.assertEqual(read.st_ctime, closed.st_ctime) self.assertLessEqual(created.st_atime, read.st_atime) self.assertEqual(read.st_atime, closed.st_atime) self.assertEqual(created.st_mtime, read.st_mtime) self.assertEqual(read.st_mtime, closed.st_mtime) def check_open_write_close_non_w_mode(self): """ When an existing file is opened with 'a', 'a+' or 'r+' mode and is then written to, st_ctime (Posix only) and st_mtime are updated close (flush), but not on opening or when written to. """ before, opened, written, closed = self.open_write_close() self.assertEqual(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, written.st_ctime) self.assertLessExceptWindows(written.st_ctime, closed.st_ctime) self.assertEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, written.st_atime) self.assertLessEqual(written.st_atime, closed.st_atime) self.assertEqual(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, written.st_mtime) self.assertLess(written.st_mtime, closed.st_mtime) def check_open_write_flush_close_w_mode(self): """ When an existing file is opened with 'w' or 'w+' mode and is then written to, st_ctime (Posix only) and st_mtime are updated on open (truncating). Under Posix, st_mtime is updated on flush, under Windows, on close instead. """ before, opened, written, flushed, closed = self.open_write_flush() self.assertLessEqual(before.st_ctime, opened.st_ctime) self.assertLessEqual(written.st_ctime, flushed.st_ctime) self.assertEqual(opened.st_ctime, written.st_ctime) self.assertEqual(flushed.st_ctime, closed.st_ctime) self.assertLessEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, written.st_atime) self.assertLessEqual(written.st_atime, flushed.st_atime) self.assertLessEqual(flushed.st_atime, closed.st_atime) self.assertLess(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, written.st_mtime) self.assertLessExceptWindows(written.st_mtime, flushed.st_mtime) self.assertLessEqual(flushed.st_mtime, closed.st_mtime) def check_open_write_flush_close_non_w_mode(self): """ When an existing file is opened with 'a', 'a+' or 'r+' mode and is then written to, st_ctime and st_mtime are updated on flush under Posix. Under Windows, only st_mtime is updated on close instead. """ before, opened, written, flushed, closed = self.open_write_flush() self.assertEqual(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, written.st_ctime) self.assertLessExceptWindows(written.st_ctime, flushed.st_ctime) self.assertEqual(flushed.st_ctime, closed.st_ctime) self.assertEqual(before.st_atime, opened.st_atime) self.assertEqual(opened.st_atime, written.st_atime) self.assertLessEqual(written.st_atime, flushed.st_atime) self.assertLessEqual(flushed.st_atime, closed.st_atime) self.assertEqual(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, written.st_mtime) self.assertLessExceptWindows(written.st_mtime, flushed.st_mtime) self.assertLessEqual(flushed.st_mtime, closed.st_mtime) class TestFakeModeW(FakeStatTestBase): def setUp(self): super(TestFakeModeW, self).setUp() self.mode = 'w' def test_open_close_new_file(self): self.check_open_close_new_file() def test_open_write_close_new_file(self): self.check_open_write_close_new_file() def test_open_close(self): self.check_open_close_w_mode() def test_open_write_close(self): self.check_open_write_close_w_mode() def test_open_flush_close(self): self.check_open_flush_close_w_mode() def test_open_write_flush_close(self): self.check_open_write_flush_close_w_mode() def test_read_raises(self): with self.open(self.file_path, 'w') as f: with self.assertRaises(OSError): f.read() class TestRealModeW(TestFakeModeW): def use_real_fs(self): return True class TestFakeModeWPlus(FakeStatTestBase): def setUp(self): super(TestFakeModeWPlus, self).setUp() self.mode = 'w+' def test_open_close_new_file(self): self.check_open_close_new_file() def test_open_write_close_new_file(self): self.check_open_write_close_new_file() def test_open_read_close_new_file(self): self.check_open_read_close_new_file() def test_open_close(self): self.check_open_close_w_mode() def test_open_write_close(self): self.check_open_write_close_w_mode() def test_open_read_close(self): """ When an existing file is opened with 'w+' mode and is then written to, st_ctime (Posix only) and st_mtime are updated on open (truncating) and again on close (flush). Under Posix, st_atime is updated on read. """ before, opened, read, closed = self.open_read_close() self.assertLessExceptWindows(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, read.st_ctime) self.assertEqual(read.st_ctime, closed.st_ctime) self.assertLessEqual(before.st_atime, opened.st_atime) self.assertLessEqual(opened.st_atime, read.st_atime) self.assertEqual(read.st_atime, closed.st_atime) self.assertLess(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, read.st_mtime) self.assertEqual(read.st_mtime, closed.st_mtime) def test_open_flush_close(self): self.check_open_flush_close_w_mode() def test_open_write_flush_close(self): self.check_open_write_flush_close_w_mode() class TestRealModeWPlus(TestFakeModeWPlus): def use_real_fs(self): return True class TestFakeModeA(FakeStatTestBase): def setUp(self): super(TestFakeModeA, self).setUp() self.mode = 'a' def test_open_close_new_file(self): self.check_open_close_new_file() def test_open_write_close_new_file(self): self.check_open_write_close_new_file() def test_open_close(self): self.check_open_close_non_w_mode() def test_open_write_close(self): self.check_open_write_close_non_w_mode() def test_open_flush_close(self): self.check_open_flush_close_non_w_mode() def test_open_write_flush_close(self): self.check_open_write_flush_close_non_w_mode() def test_read_raises(self): with self.open(self.file_path, 'a') as f: with self.assertRaises(OSError): f.read() class TestRealModeA(TestFakeModeA): def use_real_fs(self): return True class TestFakeModeAPlus(FakeStatTestBase): def setUp(self): super(TestFakeModeAPlus, self).setUp() self.mode = 'a+' def test_open_close_new_file(self): self.check_open_close_new_file() def test_open_write_close_new_file(self): self.check_open_write_close_new_file() def test_open_read_close_new_file(self): self.check_open_read_close_new_file() def test_open_close(self): self.check_open_close_non_w_mode() def test_open_write_close(self): self.check_open_write_close_non_w_mode() def test_open_read_close(self): self.check_open_read_close_non_w_mode() def test_open_flush_close(self): self.check_open_flush_close_non_w_mode() def test_open_write_flush_close(self): self.check_open_write_flush_close_non_w_mode() class TestRealModeAPlus(TestFakeModeAPlus): def use_real_fs(self): return True class TestFakeModeR(FakeStatTestBase): def setUp(self): super(TestFakeModeR, self).setUp() self.mode = 'r' def test_open_close(self): self.check_open_close_non_w_mode() def test_open_read_close(self): self.check_open_read_close_non_w_mode() def test_open_flush_close(self): self.check_open_flush_close_non_w_mode() def test_open_read_flush_close(self): """ When an existing file is opened with 'r' mode, read, flushed and closed, st_atime is updated after reading under Posix. """ before, opened, read, flushed, closed = self.open_read_flush() self.assertEqual(before.st_ctime, opened.st_ctime) self.assertEqual(opened.st_ctime, read.st_ctime) self.assertEqual(read.st_ctime, flushed.st_ctime) self.assertEqual(flushed.st_ctime, closed.st_ctime) self.assertEqual(before.st_atime, opened.st_atime) self.assertLessEqual(opened.st_atime, read.st_atime) self.assertEqual(read.st_atime, flushed.st_atime) self.assertEqual(flushed.st_atime, closed.st_atime) self.assertEqual(before.st_mtime, opened.st_mtime) self.assertEqual(opened.st_mtime, read.st_mtime) self.assertEqual(read.st_mtime, flushed.st_mtime) self.assertEqual(flushed.st_mtime, closed.st_mtime) def test_open_not_existing_raises(self): with self.assertRaises(OSError): with self.open(self.file_path, 'r'): pass class TestRealModeR(TestFakeModeR): def use_real_fs(self): return True class TestFakeModeRPlus(FakeStatTestBase): def setUp(self): super(TestFakeModeRPlus, self).setUp() self.mode = 'r+' def test_open_close(self): self.check_open_close_non_w_mode() def test_open_write_close(self): self.check_open_write_close_non_w_mode() def test_open_read_close(self): self.check_open_read_close_non_w_mode() def test_open_flush_close(self): self.check_open_flush_close_non_w_mode() def test_open_write_flush_close(self): self.check_open_write_flush_close_non_w_mode() def test_open_not_existing_raises(self): with self.assertRaises(OSError): with self.open(self.file_path, 'r+'): pass class TestRealModeRPlus(TestFakeModeRPlus): def use_real_fs(self): return True if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fake_tempfile_test.py0000666000000000000000000001044313757263357020670 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Tests that ensure that the `tempfile` module works with `fake_filesystem` if using `Patcher` (via `fake_filesystem_unittest`). """ import os import stat import tempfile import unittest from pyfakefs import fake_filesystem_unittest class FakeTempfileModuleTest(fake_filesystem_unittest.TestCase): """Test the 'tempfile' module with the fake file system.""" def setUp(self): self.setUpPyfakefs() def test_named_temporary_file(self): obj = tempfile.NamedTemporaryFile() self.assertTrue(self.fs.get_object(obj.name)) obj.close() with self.assertRaises(OSError): self.fs.get_object(obj.name) def test_named_temporary_file_no_delete(self): obj = tempfile.NamedTemporaryFile(delete=False) obj.write(b'foo') obj.close() file_obj = self.fs.get_object(obj.name) contents = file_obj.contents self.assertEqual('foo', contents) obj = tempfile.NamedTemporaryFile(mode='w', delete=False) obj.write('foo') obj.close() file_obj = self.fs.get_object(obj.name) self.assertEqual('foo', file_obj.contents) def test_mkstemp(self): next_fd = len(self.fs.open_files) temporary = tempfile.mkstemp() self.assertEqual(2, len(temporary)) self.assertTrue( temporary[1].startswith( os.path.join(tempfile.gettempdir(), 'tmp'))) self.assertEqual(next_fd, temporary[0]) self.assertTrue(self.fs.exists(temporary[1])) mode = 0o666 if self.fs.is_windows_fs else 0o600 self.assertEqual(self.fs.get_object(temporary[1]).st_mode, stat.S_IFREG | mode) fh = os.fdopen(temporary[0], 'w+b') self.assertEqual(temporary[0], fh.fileno()) def test_mkstemp_dir(self): """test tempfile.mkstemp(dir=).""" # expect fail: /dir does not exist with self.assertRaises(OSError): tempfile.mkstemp(dir='/dir') # expect pass: /dir exists self.fs.create_dir('/dir') next_fd = len(self.fs.open_files) temporary = tempfile.mkstemp(dir='/dir') self.assertEqual(2, len(temporary)) self.assertEqual(next_fd, temporary[0]) self.assertTrue(temporary[1].startswith( os.path.join(os.sep, 'dir', 'tmp'))) self.assertTrue(self.fs.exists(temporary[1])) mode = 0o666 if self.fs.is_windows_fs else 0o600 self.assertEqual(self.fs.get_object(temporary[1]).st_mode, stat.S_IFREG | mode) def test_mkdtemp(self): dirname = tempfile.mkdtemp() self.assertTrue(dirname) self.assertTrue(self.fs.exists(dirname)) self.assertEqual(self.fs.get_object(dirname).st_mode, stat.S_IFDIR | 0o700) def test_temporary_directory(self): with tempfile.TemporaryDirectory() as tmpdir: self.assertTrue(tmpdir) self.assertTrue(self.fs.exists(tmpdir)) self.assertEqual(self.fs.get_object(tmpdir).st_mode, stat.S_IFDIR | 0o700) def test_temporary_file(self): with tempfile.TemporaryFile() as f: f.write(b'test') f.seek(0) self.assertEqual(b'test', f.read()) def test_temporay_file_with_dir(self): with self.assertRaises(FileNotFoundError): tempfile.TemporaryFile(dir='/parent') os.mkdir('/parent') with tempfile.TemporaryFile() as f: f.write(b'test') f.seek(0) self.assertEqual(b'test', f.read()) if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/fixtures/0000777000000000000000000000000014167600566016324 5ustar 00000000000000pyfakefs-4.5.4/pyfakefs/tests/fixtures/config_module.py0000666000000000000000000000004614147521400021473 0ustar 00000000000000configurable_value = 'another value' pyfakefs-4.5.4/pyfakefs/tests/fixtures/deprecated_property.py0000666000000000000000000000203114147521400022721 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Used for testing suppression of deprecation warnings while iterating over modules. The code is modeled after code in xmlbuilder.py in Python 3.6. See issue #542. """ import warnings class DeprecatedProperty: def __get__(self, instance, cls): warnings.warn("async is deprecated", DeprecationWarning) warnings.warn("async will be replaced", FutureWarning) return instance class DeprecationTest: locals()['async'] = DeprecatedProperty() pyfakefs-4.5.4/pyfakefs/tests/fixtures/excel_test.xlsx0000666000000000000000000001126613757263357021420 0ustar 00000000000000PKW›ÊP _rels/.rels­’ÏjÃ0 ‡ï} £{ã´…1Fœ^Ê ·2ºÐl%1‰ÿ`k[öö3c°u”°ÁŽ–úô!Ôìg7‰JÙ¯`SÕ Èë`¬ï<žï×·°oWÍMÈ%’³(=>+˜ã”Yä0W!’/?]H¹ôÐ^töìîaqsj¤wc…V ‹F!ó@ºj›°o™?cžE®J.µ‚„Á²›å»ÅQ›]®õΣûÊ&¬FlçA`‹nGºE‘J›†#™fØÖ/m € â0œ Šõsó?ºªDk]ìP؃‰½­EkÙrQ O}CoÛϼ!Ú)— –¯´¿/çÅnßf”°ŠK Ôh­_òïP uÄ¥d^ÉRÝ´T!RàyÈýK#]¡zäìOŽöw¼3ô6âE+ärS-eÂÐì/e‰1Šâ_‘M7±GžÛÁyzªÔDŽѮÎoÎGw|%Ö´Ééxv5øî@lkLØ,ºŽ™‡<è&–°IH×*a,º"…S' zE o:r˾žr“¥L¢Ž,yïKªí$ƒ<+rIœÍ\PÀÜ—±Ã€¨á‚V!Œô^‰¨ce ú¤K‚XÚ%þº§‹½‰œhŽÂ0ê`á„-ºïETRÓù/aI‘è¥äTż½ ûñ~OÓÙ4öãU4ö£èvâ_Müì6Ëhté:½Î~’Âꜞ´§oÑÐïòÕæLË=õj[9JeõoÇ,4±üPKˆG°àþyPKW›ÊP xl/styles.xmlíXOOÛ0½ïSX¾$%˜Ò ÆÔi— "!M;˜ÄI,ü'²]høôû9NÓ„Â&u‡©'Û/¿÷üòì¨v“‹•àè‘jÔœáè(ĈÊLåL–3|»˜<ÃÈX"s•¤3ÜPƒ/Ò‰± §7¥‚43\Y[ “UTs¤j*áI¡´ †º L­)É# LÂpÂ$N¹sa ÊÔRZ°ÑCÈ7ßr§1F^îJå`å+•TŽƒ4 :4)”ÜèÄØibžÑ#á ºrIõãKͼBAã'­¤'î@÷†Þ6.ÆyÊ{ Mjb-ÕrÔõM ÉJXj/ÓÖý¥ºÔ¤‰&'BÛÀ¼÷Jç°µ†Ëê!”3R*Iøm=Ãá†âú¢žäLN š••k­ª'b­ÐYsÜÔ^¹ïÀôåüÆíÓ»bóö!ˆ®Ší}%Ûlç½ëz¥n@êš7såD¬^ÒøÜ–Œ KÎJ)è‹Âk­,Ílû™µpšu!ª”fÏ í°ì¶µû*-Ëäß#KWö‡²Ä«€§'Mê€}ˆLæíÄðÌTšÉ‡…š³þ1ÄT÷6WÙÍ×&+–uP¬ŠI…›œ¢]sê|¾ j“Zoƒ÷cfr0󆙿­ƒ™ƒ™ƒ™ƒ™ƒ™]ÌÄÇûôKG{å&Þ+7“}rsþŸÍÃã»?ÌÎñÑ®ÇøU±í|èç­¿ƒ3}ÐE9¸ õ±NñEîª9ÃßÝ›’»_2n™ô£`›p¥„ ëúèdD8~“€~†¿zÒtDš¾JZjMeÖôœÓ'þg4×Ùˆwúïšê Ö §œ(þê» ›¿GÒßPKŒO†ƒcPKW›ÊPxl/worksheets/sheet1.xml½VMsÛ6½÷WpxÈ©%:²S‡bÆ•ª&3Žå‰œf&7ˆXŠƒX¥Ø¿> ðÓR§Óé!:XÂîb÷í[àÁÉ»ï¥  @µg“i€Ê µ_„ŸÖ¿½ c™âL¢‚Eø&|—þ’Q?šÀ”@™EXX[]G‘É (™™`Š<9ê’YZê}d* ŒûM¥Œâéô2*™Pa“áZÿ—˜ç"ƒfu Ê6I4Hf ¾)DeÂ4ñîu iADN°s& ¯b{Ø‚ý\y¿}À{2tî(M¢vsšpA+†|ÞÌ®W±‹ð 8šÑïÀx\ÎZ2Ó¥óÆ¿´à·BY­®[ã'<.Q¾'6ˆø±ã+hì Zì Bx ¹íSZ¶Û‚„ÌïÛÔVR‘íS¹CÙ'à³ZZÊ¡îìB¼•£URJ¬\‰%HéÚ ƒÌÅ~ ü—¯Ãà±ÜfLI³ét´¾óÛO­ŽÎ[ö„µ§¥õº³²C|t&—wê†ä»pôVÌ«E0² E×ÍÖÀ|kór‰Ç¿»Ñ¬ýÉ¡Q·L _·ášMæóÙü2ž÷<ÑTÞƒãœÜñ„Îý3M£³´ücCô-@R¼4¶Q…¦¿è€ÏŠY–&ÍÂ1]‹eÔ—ê‚sP½£‰þ@ MO²Ê¸óÑúÌ•s“5>‚6+²ÒYiÖFüq¿ŒXžG\¼ŒXG¼î#"ê¼o?þÉíÇ#`Æ›ž´qBÐò<â„ ÕyÄÅIûÑè$TZ(»©¼xÉ©î ûA:N-$aÝe.P‹gT–É%)"è¡s'ëVd玨ÑÁLï–^`¦“«7WóVu†%ÝKÿ,Ìã«þC³Ù¡¥aü“§ðª6$Èíhõ\Wtõ+Ð[ñL÷ÿw"n$3¹ÐÆ:1¹«Ë4·ÅëuwÛeaÃÀ¥Ýh_›ãQ= 6Ä-ˆÿD, µÕLÐì$ËoÿRÛ?×l$·ÉÎK÷Ö§˜Šlµõ)ºÓQ¬*±/\#Ý K†•p39.¶Öž£€‹<§9)ëó:ó†ó?ÃHä¼yDÒW¬¬Þ.ýßWßj´oèù2Á=MŸ°dê×­Ã5o¼>pû¯›$ò¸” šÿ™r Äfà÷>q›-‰Æ­Ò²ÿ"ýPKÇŽ5„„PKW›ÊPxl/sharedStrings.xmlÏÁ Â0 à»OQrw""£­Á'Ш]Ü k:›Nôí­‚x“ò%?QûGÄûHVU ÉÅÖS§á|:.w 8[jí 5<‘aoŠ9‹²J¬¡Ïyl¤d×c°\Å©L®1›KLä1¡m¹GÌaëºÞÊ`=pq¢¬ab"›ððÍF±7êSÑðh]i.7ÓÁX%³Qò-þ¨Ë,åf©ö§dùܼPK[Æò¬7PKW›ÊPxl/_rels/workbook.xml.rels­‘MkÃ0 †ïýF÷ÅIcŒ8½ŒA¯ýøÆQâÐÄ6’Öµÿ~.[ eìГÐ×ó¾Hõê4êˆÄC ª¢…ÁÅv½ýîíáVÍ¢Þàh%°«¼Ø€I/Z³ó8Y.bÂ;]¤ÉJN©×ɺƒíQ/ËòIÓœÍS­[´n+P»sÂÿ°c× _£{Ÿ0È Ír‘3ÑRbà+/2ômùå=å?"Ø#ʯƒŸR6w Õ_fïz o Û­P~ìü$óò·™E­¯ÞÝ|PKOðùzÒ%PKW›ÊPdocProps/core.xmlRËNÃ0¼ó‘"d%©¥'*!QâfìmjˆËv_“4i¸íìŒg_Χ{Y[0V4ª@I£k¸PU^–óðÖQÅiÝ((Ð,š–W9Ó„5žL£Á86ðFʦ ´vNŒ-[ƒ¤6ò åÉUc$uš kʾh8ã Kp”SGqkêÑ-9-õÆÔgj œÅI”à“Ö‘ö⃎9SJá.JrTï­…»Ý.ÚM:©ï?Áo‹ÇçnÔP¨vU P™!ÌuÀo@úró:¹Ÿ-ç¨Lã4ã,Lâešô†L’÷ÿzßöqcÊ–=s°Ìíü {òGÂ㚪jã^rgdLµ§¬©u ô•~wðrCGò˜ûÿH¹ÎÎF ºÊ¶¢ý{eÒaÛµÝ||sýH#𱮆>=„þcù PK-ÇÖÆaÛPKW›ÊPdocProps/app.xmlMOÃ0 †ïüŠ*⺥_k»)Í„„8!Á¡|ܪ4q· æCÛ¿'€´íŒ|ñk[Ïk›mfJ0ílK²eJ°Ò)mw-yé I «Ää,´älù {ž‡5„$lhÉÑo( rF„elÛØÝlF9ï¨G-áÞÉOiž¦…#‚U þ $ÄÍÿ UNþì^»“<Î:0~œÑKÚ9S§ ð,–Ï‚Ýy?i)0~„?êa†§_ Z-‹ùí›¶Ê}…þ½©úªL®FúxÄH¤¢*ã"jL‹¬,†z݈Qe ëRd¹Vi±Z7uŠÑk3F/_åßPK°"‚ÙšPKW›ÊP[Content_Types].xml½”ËNÃ0E÷ýŠÈ[»eJÒ%T¢¬‘‰'‰iüí–öï§PU%´ "VV'MöŠ1_6 ¸§Æ‚ÆHeœâ?]Í,/¼v>_°Òè:¤!z"»Š/ÛÜ®q{ËE9I®·y•nm+K0Ìb”õê´þˆp¥ÅAuéGe•]Žo¤õg߬®RÅÎâ~¿âÕB¿¤  æÇí¤€dÆ]¸ç Øsì„Ñûé#­[öfÜâŘ=>öš©*Y‚0åR¡„zë€ ßÕÒn¥ŠK}‚ïæ?4½3ýAçÀ³n™ \ÄÎÿÔî@<‡×lðAì{Ÿ¨c{ìöÏÃA,|æŒõø28ø}÷Ÿ¼¨N- òø¯ßÑúÏã†x׈¯ìQƺ‡²xPK”²Ìq]WPKW›ÊPk¿vßB _rels/.relsPKW›ÊPˆG°àþyxl/workbook.xmlPKW›ÊPŒO†ƒc Sxl/styles.xmlPKW›ÊPÇŽ5„„xl/worksheets/sheet1.xmlPKW›ÊP[Æò¬7Û xl/sharedStrings.xmlPKW›ÊPOðùzÒ%É xl/_rels/workbook.xml.relsPKW›ÊP-ÇÖÆaÛã docProps/core.xmlPKW›ÊP°"‚Ùšƒ docProps/app.xmlPKW›ÊP”²Ìq]WÃ[Content_Types].xmlPK ?apyfakefs-4.5.4/pyfakefs/tests/fixtures/module_with_attributes.py0000666000000000000000000000237113757263357023476 0ustar 00000000000000# Copyright 2017 John McGehee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """This module is for testing pyfakefs :py:class:`fake_filesystem_unittest.Patcher`. It defines attributes that have the same names as file modules, sudh as 'io` and `path`. Since these are not modules, :py:class:`fake_filesystem_unittest.Patcher` should not patch them. Whenever a new module is added to :py:meth:`fake_filesystem_unittest.Patcher._findModules`, the corresponding attribute should be added here and in the test :py:class:`fake_filesystem_unittest_test.TestAttributesWithFakeModuleNames`. """ os = 'os attribute value' path = 'path attribute value' pathlib = 'pathlib attribute value' shutil = 'shutil attribute value' io = 'io attribute value' pyfakefs-4.5.4/pyfakefs/tests/fixtures/__init__.py0000666000000000000000000000000013757263357020432 0ustar 00000000000000pyfakefs-4.5.4/pyfakefs/tests/import_as_example.py0000666000000000000000000000556714147521400020535 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Example module that is used for testing modules that import file system modules to be patched under another name. """ import os as my_os import pathlib import sys from builtins import open as bltn_open from io import open as io_open from os import path from os import stat from os import stat as my_stat from os.path import exists from os.path import exists as my_exists from pathlib import Path def check_if_exists1(filepath): # test patching module imported under other name return my_os.path.exists(filepath) def check_if_exists2(filepath): # tests patching path imported from os return path.exists(filepath) def check_if_exists3(filepath): # tests patching Path imported from pathlib return Path(filepath).exists() def check_if_exists4(filepath, file_exists=my_os.path.exists): return file_exists(filepath) def check_if_exists5(filepath): # tests patching `exists` imported from os.path return exists(filepath) def check_if_exists6(filepath): # tests patching `exists` imported from os.path as other name return my_exists(filepath) def check_if_exists7(filepath): # tests patching pathlib return pathlib.Path(filepath).exists() def file_stat1(filepath): # tests patching `stat` imported from os return stat(filepath) def file_stat2(filepath): # tests patching `stat` imported from os as other name return my_stat(filepath) def system_stat(filepath): if sys.platform == 'win32': from nt import stat as system_stat else: from posix import stat as system_stat return system_stat(filepath) def file_contents1(filepath): with bltn_open(filepath) as f: return f.read() def file_contents2(filepath): with io_open(filepath) as f: return f.read() def exists_this_file(): """Returns True in real fs only""" return exists(__file__) def open_this_file(): """Works only in real fs""" with open(__file__): pass def return_this_file_path(): """Works only in real fs""" return Path(__file__) class TestDefaultArg: def check_if_exists(self, filepath, file_exists=my_os.path.exists): # this is a similar case as in the tempfile implementation under Posix return file_exists(filepath) pyfakefs-4.5.4/pyfakefs/tests/logsio.py0000666000000000000000000000152614147521400016310 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Example module that is used for a regression test where a module with a name ending with "io" was skipped from patching (see #569). """ def file_contents(path): """Return the contents of the given path as byte array.""" with open(path, 'rb') as f: return f.read() pyfakefs-4.5.4/pyfakefs/tests/mox3_stubout_example.py0000666000000000000000000000163313757263357021225 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Example module that is used for testing the functionality of :py:class`pyfakefs.mox_stubout.StubOutForTesting`. """ import datetime import math import os def check_if_exists(filepath): return os.path.exists(filepath) def fabs(x): return math.fabs(x) def tomorrow(): return datetime.date.today() + datetime.timedelta(days=1) pyfakefs-4.5.4/pyfakefs/tests/mox3_stubout_test.py0000666000000000000000000001242113757263357020546 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Unit tests for mox3_stubout.""" import datetime import math import os import unittest from os import path from pyfakefs import mox3_stubout from pyfakefs.tests import mox3_stubout_example class NoPanicMath: real_math = math @staticmethod def fabs(_x): return 42 def __getattr__(self, name): """Forwards any unfaked calls to the standard module.""" return getattr(self.real_math, name) class ExistingPath: real_path = path @staticmethod def exists(_path): return True def __getattr__(self, name): """Forwards any unfaked calls to the standard module.""" return getattr(self.real_path, name) class GroundhogDate(datetime.date): @classmethod def today(cls): return datetime.date(1993, 2, 2) class StubOutForTestingTest(unittest.TestCase): def setUp(self): super(StubOutForTestingTest, self).setUp() self.stubber = mox3_stubout.StubOutForTesting() def test_stubout_method_with_set(self): non_existing_path = 'non_existing_path' self.assertFalse( mox3_stubout_example.check_if_exists(non_existing_path)) self.stubber.set(os.path, 'exists', lambda x: True) self.assertTrue( mox3_stubout_example.check_if_exists(non_existing_path)) self.stubber.unset_all() self.assertFalse( mox3_stubout_example.check_if_exists(non_existing_path)) def test_stubout_class_with_set(self): self.assertGreater(mox3_stubout_example.tomorrow().year, 2000) self.stubber.set(datetime, 'date', GroundhogDate) self.assertEqual(mox3_stubout_example.tomorrow(), datetime.date(1993, 2, 3)) self.stubber.unset_all() self.assertGreater(mox3_stubout_example.tomorrow().year, 2000) def test_stubout_module_with_set(self): self.assertEqual(10, mox3_stubout_example.fabs(-10)) self.stubber.set(mox3_stubout_example, 'math', NoPanicMath) self.assertEqual(42, mox3_stubout_example.fabs(-10)) self.stubber.unset_all() self.assertEqual(10, mox3_stubout_example.fabs(-10)) def test_set_raise_if_unknown_attribute(self): self.assertRaises(AttributeError, self.stubber.set, os.path, 'exists_not', lambda x: True) self.assertRaises(AttributeError, self.stubber.set, datetime, 'tomorrow', GroundhogDate) self.assertRaises(AttributeError, self.stubber.set, mox3_stubout_example, 'math1', NoPanicMath) def test_stubout_method_with_smart_set(self): non_existing_path = 'non_existing_path' self.stubber.smart_set(os.path, 'exists', lambda x: True) self.assertTrue( mox3_stubout_example.check_if_exists(non_existing_path)) self.stubber.smart_unset_all() self.assertFalse( mox3_stubout_example.check_if_exists(non_existing_path)) def test_stubout_class_with_smart_set(self): self.stubber.smart_set(datetime, 'date', GroundhogDate) self.assertEqual(mox3_stubout_example.tomorrow(), datetime.date(1993, 2, 3)) self.stubber.smart_unset_all() self.assertGreater(mox3_stubout_example.tomorrow().year, 2000) def test_stubout_module_with_smart_set(self): self.assertEqual(10, mox3_stubout_example.fabs(-10)) self.stubber.smart_set(mox3_stubout_example, 'math', NoPanicMath) self.assertEqual(42, mox3_stubout_example.fabs(-10)) self.stubber.smart_unset_all() self.assertEqual(10, mox3_stubout_example.fabs(-10)) def test_stubout_submodule_with_smart_set(self): # this one does not work with Set non_existing_path = 'non_existing_path' self.assertFalse( mox3_stubout_example.check_if_exists(non_existing_path)) self.stubber.smart_set(os, 'path', ExistingPath) self.assertTrue( mox3_stubout_example.check_if_exists(non_existing_path)) self.stubber.smart_unset_all() self.assertFalse( mox3_stubout_example.check_if_exists(non_existing_path)) def test_smart_set_raise_if_unknown_attribute(self): self.assertRaises(AttributeError, self.stubber.smart_set, os.path, 'exists_not', lambda x: True) self.assertRaises(AttributeError, self.stubber.smart_set, datetime, 'tomorrow', GroundhogDate) self.assertRaises(AttributeError, self.stubber.smart_set, mox3_stubout_example, 'math1', NoPanicMath) if __name__ == '__main__': unittest.main() pyfakefs-4.5.4/pyfakefs/tests/patched_packages_test.py0000666000000000000000000000464714147521400021330 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """ Provides patches for some commonly used modules that enable them to work with pyfakefs. """ import os from pyfakefs import fake_filesystem_unittest try: import pandas as pd except ImportError: pd = None try: import xlrd except ImportError: xlrd = None try: import openpyxl except ImportError: openpyxl = None class TestPatchedPackages(fake_filesystem_unittest.TestCase): def setUp(self): self.setUpPyfakefs() if pd is not None: def test_read_csv(self): path = '/foo/bar.csv' self.fs.create_file(path, contents='1,2,3,4') df = pd.read_csv(path) assert (df.columns == ['1', '2', '3', '4']).all() def test_read_table(self): path = '/foo/bar.csv' self.fs.create_file(path, contents='1|2|3|4') df = pd.read_table(path, delimiter='|') assert (df.columns == ['1', '2', '3', '4']).all() if pd is not None and xlrd is not None: def test_read_excel(self): path = '/foo/bar.xlsx' src_path = os.path.dirname(os.path.abspath(__file__)) src_path = os.path.join(src_path, 'fixtures', 'excel_test.xlsx') # map the file into another location to be sure that # the real fs is not used self.fs.add_real_file(src_path, target_path=path) df = pd.read_excel(path) assert (df.columns == [1, 2, 3, 4]).all() if pd is not None and openpyxl is not None: def test_write_excel(self): self.fs.create_dir('/foo') path = '/foo/bar.xlsx' df = pd.DataFrame([[0, 1, 2, 3]]) with pd.ExcelWriter(path) as writer: df.to_excel(writer) df = pd.read_excel(path) assert (df.columns == ['Unnamed: 0', 0, 1, 2, 3]).all() pyfakefs-4.5.4/pyfakefs/tests/performance_test.py0000666000000000000000000000503614147521400020354 0ustar 00000000000000# Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Shall provide tests to check performance overhead of pyfakefs.""" import os import time import unittest from pyfakefs.fake_filesystem_unittest import TestCase from pyfakefs.helpers import IS_PYPY if os.environ.get('TEST_PERFORMANCE'): class SetupPerformanceTest(TestCase): @classmethod def setUpClass(cls) -> None: cls.start_time = time.time() @classmethod def tearDownClass(cls) -> None: cls.elapsed_time = time.time() - cls.start_time print('Elapsed time per test for cached setup: {:.3f} ms'.format( cls.elapsed_time * 10)) def setUp(self) -> None: self.setUpPyfakefs() class SetupNoCachePerformanceTest(TestCase): @classmethod def setUpClass(cls) -> None: cls.start_time = time.time() @classmethod def tearDownClass(cls) -> None: cls.elapsed_time = time.time() - cls.start_time print('Elapsed time per test for uncached setup: {:.3f} ms'.format( cls.elapsed_time * 10)) def setUp(self) -> None: self.setUpPyfakefs(use_cache=False) @unittest.skipIf(IS_PYPY, 'PyPy times are not comparable') class TimePerformanceTest(TestCase): """Make sure performance degradation in setup is noticed. The numbers are related to the CI builds and may fail in local builds. """ def test_cached_time(self): self.assertLess(SetupPerformanceTest.elapsed_time, 0.4) def test_uncached_time(self): self.assertLess(SetupNoCachePerformanceTest.elapsed_time, 6) def test_setup(self): pass for n in range(100): test_name = "test_" + str(n) setattr(SetupPerformanceTest, test_name, test_setup) test_name = "test_nocache" + str(n) setattr(SetupNoCachePerformanceTest, test_name, test_setup) if __name__ == "__main__": unittest.main() pyfakefs-4.5.4/pyfakefs/tests/test_utils.py0000666000000000000000000004043714147521400017217 0ustar 00000000000000# Copyright 2009 Google Inc. All Rights Reserved. # Copyright 2015-2017 John McGehee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Common helper classes used in tests, or as test class base.""" import os import platform import shutil import stat import sys import tempfile import unittest from unittest import mock from pyfakefs import fake_filesystem from pyfakefs.helpers import is_byte_string, to_string class DummyTime: """Mock replacement for time.time. Increases returned time on access.""" def __init__(self, curr_time, increment): self.current_time = curr_time self.increment = increment def __call__(self, *args, **kwargs): current_time = self.current_time self.current_time += self.increment return current_time class DummyMock: def start(self): pass def stop(self): pass def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): pass def time_mock(start=200, step=20): return mock.patch('pyfakefs.fake_filesystem.now', DummyTime(start, step)) class TestCase(unittest.TestCase): """Test base class with some convenience methods and attributes""" is_windows = sys.platform == 'win32' is_cygwin = sys.platform == 'cygwin' is_macos = sys.platform == 'darwin' symlinks_can_be_tested = None def assert_mode_equal(self, expected, actual): return self.assertEqual(stat.S_IMODE(expected), stat.S_IMODE(actual)) def assert_raises_os_error(self, subtype, expression, *args, **kwargs): """Asserts that a specific subtype of OSError is raised.""" try: expression(*args, **kwargs) self.fail('No exception was raised, OSError expected') except OSError as exc: if isinstance(subtype, list): self.assertIn(exc.errno, subtype) else: self.assertEqual(subtype, exc.errno) class RealFsTestMixin: """Test mixin to allow tests to run both in the fake filesystem and in the real filesystem. To run tests in the real filesystem, a new test class can be derived from the test class testing the fake filesystem which overwrites `use_real_fs()` to return `True`. All tests in the real file system operate inside the local temp path. In order to make a test able to run in the real FS, it must not use the fake filesystem functions directly. For access to `os` and `open`, the respective attributes must be used, which point either to the native or to the fake modules. A few convenience methods allow to compose paths, create files and directories. """ def __init__(self): self.filesystem = None self.open = open self.os = os self.base_path = None def setUp(self): if not os.environ.get('TEST_REAL_FS'): self.skip_real_fs() if self.use_real_fs(): self.base_path = tempfile.mkdtemp() def tearDown(self): if self.use_real_fs(): self.os.chdir(os.path.dirname(self.base_path)) shutil.rmtree(self.base_path, ignore_errors=True) os.chdir(self.cwd) @property def is_windows_fs(self): return TestCase.is_windows def set_windows_fs(self, value): if self.filesystem is not None: self.filesystem.is_windows_fs = value if value: self.filesystem.is_macos = False self.create_basepath() @property def is_macos(self): return TestCase.is_macos @property def is_pypy(self): return platform.python_implementation() == 'PyPy' def use_real_fs(self): """Return True if the real file system shall be tested.""" return False def path_separator(self): """Can be overwritten to use a specific separator in the fake filesystem.""" if self.use_real_fs(): return os.path.sep return '/' def check_windows_only(self): """If called at test start, the real FS test is executed only under Windows, and the fake filesystem test emulates a Windows system. """ if self.use_real_fs(): if not TestCase.is_windows: raise unittest.SkipTest( 'Testing Windows specific functionality') else: self.set_windows_fs(True) def check_linux_only(self): """If called at test start, the real FS test is executed only under Linux, and the fake filesystem test emulates a Linux system. """ if self.use_real_fs(): if TestCase.is_macos or TestCase.is_windows: raise unittest.SkipTest( 'Testing Linux specific functionality') else: self.set_windows_fs(False) self.filesystem.is_macos = False def check_macos_only(self): """If called at test start, the real FS test is executed only under MacOS, and the fake filesystem test emulates a MacOS system. """ if self.use_real_fs(): if not TestCase.is_macos: raise unittest.SkipTest( 'Testing MacOS specific functionality') else: self.set_windows_fs(False) self.filesystem.is_macos = True def check_linux_and_windows(self): """If called at test start, the real FS test is executed only under Linux and Windows, and the fake filesystem test emulates a Linux system under MacOS. """ if self.use_real_fs(): if TestCase.is_macos: raise unittest.SkipTest( 'Testing non-MacOs functionality') else: self.filesystem.is_macos = False def check_case_insensitive_fs(self): """If called at test start, the real FS test is executed only in a case-insensitive FS (e.g. Windows or MacOS), and the fake filesystem test emulates a case-insensitive FS under the running OS. """ if self.use_real_fs(): if not TestCase.is_macos and not TestCase.is_windows: raise unittest.SkipTest( 'Testing case insensitive specific functionality') else: self.filesystem.is_case_sensitive = False def check_case_sensitive_fs(self): """If called at test start, the real FS test is executed only in a case-sensitive FS (e.g. under Linux), and the fake file system test emulates a case-sensitive FS under the running OS. """ if self.use_real_fs(): if TestCase.is_macos or TestCase.is_windows: raise unittest.SkipTest( 'Testing case sensitive specific functionality') else: self.filesystem.is_case_sensitive = True def check_posix_only(self): """If called at test start, the real FS test is executed only under Linux and MacOS, and the fake filesystem test emulates a Linux system under Windows. """ if self.use_real_fs(): if TestCase.is_windows: raise unittest.SkipTest( 'Testing Posix specific functionality') else: self.set_windows_fs(False) def skip_real_fs(self): """If called at test start, no real FS test is executed.""" if self.use_real_fs(): raise unittest.SkipTest('Only tests fake FS') def skip_real_fs_failure(self, skip_windows=True, skip_posix=True, skip_macos=True, skip_linux=True): """If called at test start, no real FS test is executed for the given conditions. This is used to mark tests that do not pass correctly under certain systems and shall eventually be fixed. """ if True: if (self.use_real_fs() and (TestCase.is_windows and skip_windows or not TestCase.is_windows and skip_macos and skip_linux or TestCase.is_macos and skip_macos or not TestCase.is_windows and not TestCase.is_macos and skip_linux or not TestCase.is_windows and skip_posix)): raise unittest.SkipTest( 'Skipping because FakeFS does not match real FS') def symlink_can_be_tested(self, force_real_fs=False): """Used to check if symlinks and hard links can be tested under Windows. All tests are skipped under Windows for Python versions not supporting links, and real tests are skipped if running without administrator rights. """ if (not TestCase.is_windows or (not force_real_fs and not self.use_real_fs())): return True if TestCase.symlinks_can_be_tested is None: if force_real_fs: self.base_path = tempfile.mkdtemp() link_path = self.make_path('link') try: self.os.symlink(self.base_path, link_path) TestCase.symlinks_can_be_tested = True self.os.remove(link_path) except (OSError, NotImplementedError): TestCase.symlinks_can_be_tested = False if force_real_fs: self.base_path = None return TestCase.symlinks_can_be_tested def skip_if_symlink_not_supported(self, force_real_fs=False): """If called at test start, tests are skipped if symlinks are not supported.""" if not self.symlink_can_be_tested(force_real_fs): raise unittest.SkipTest( 'Symlinks under Windows need admin privileges') def make_path(self, *args): """Create a path with the given component(s). A base path is prepended to the path which represents a temporary directory in the real FS, and a fixed path in the fake filesystem. Always use to compose absolute paths for tests also running in the real FS. """ if isinstance(args[0], (list, tuple)): path = self.base_path for arg in args[0]: path = self.os.path.join(path, to_string(arg)) return path args = [to_string(arg) for arg in args] return self.os.path.join(self.base_path, *args) def create_dir(self, dir_path): """Create the directory at `dir_path`, including subdirectories. `dir_path` shall be composed using `make_path()`. """ existing_path = dir_path components = [] while existing_path and not self.os.path.exists(existing_path): existing_path, component = self.os.path.split(existing_path) if not component and existing_path: # existing path is a drive or UNC root if not self.os.path.exists(existing_path): self.filesystem.add_mount_point(existing_path) break components.insert(0, component) for component in components: existing_path = self.os.path.join(existing_path, component) self.os.mkdir(existing_path) self.os.chmod(existing_path, 0o777) def create_file(self, file_path, contents=None, encoding=None, perm=0o666): """Create the given file at `file_path` with optional contents, including subdirectories. `file_path` shall be composed using `make_path()`. """ self.create_dir(self.os.path.dirname(file_path)) mode = ('wb' if encoding is not None or is_byte_string(contents) else 'w') if encoding is not None and contents is not None: contents = contents.encode(encoding) with self.open(file_path, mode) as f: if contents is not None: f.write(contents) self.os.chmod(file_path, perm) def create_symlink(self, link_path, target_path): """Create the path at `link_path`, and a symlink to this path at `target_path`. `link_path` shall be composed using `make_path()`. """ self.create_dir(self.os.path.dirname(link_path)) self.os.symlink(target_path, link_path) def check_contents(self, file_path, contents): """Compare `contents` with the contents of the file at `file_path`. Asserts equality. """ mode = 'rb' if is_byte_string(contents) else 'r' with self.open(file_path, mode) as f: self.assertEqual(contents, f.read()) def create_basepath(self): """Create the path used as base path in `make_path`.""" if self.filesystem is not None: old_base_path = self.base_path self.base_path = self.filesystem.path_separator + 'basepath' if self.is_windows_fs: self.base_path = 'C:' + self.base_path if old_base_path != self.base_path: if old_base_path is not None: self.filesystem.reset() if not self.filesystem.exists(self.base_path): self.filesystem.create_dir(self.base_path) if old_base_path is not None: self.setUpFileSystem() def assert_equal_paths(self, actual, expected): if self.is_windows: actual = str(actual).replace('\\\\?\\', '') expected = str(expected).replace('\\\\?\\', '') if os.name == 'nt' and self.use_real_fs(): # work around a problem that the user name, but not the full # path is shown as the short name self.assertEqual(self.path_with_short_username(actual), self.path_with_short_username(expected)) else: self.assertEqual(actual, expected) elif self.is_macos: self.assertEqual(str(actual).replace('/private/var/', '/var/'), str(expected).replace('/private/var/', '/var/')) else: self.assertEqual(actual, expected) @staticmethod def path_with_short_username(path): components = path.split(os.sep) if len(components) >= 3: components[2] = components[2][:6].upper() + '~1' return os.sep.join(components) def mock_time(self, start=200, step=20): if not self.use_real_fs(): return mock.patch('pyfakefs.fake_filesystem.now', DummyTime(start, step)) return DummyMock() class RealFsTestCase(TestCase, RealFsTestMixin): """Can be used as base class for tests also running in the real file system.""" def __init__(self, methodName='runTest'): TestCase.__init__(self, methodName) RealFsTestMixin.__init__(self) def setUp(self): RealFsTestMixin.setUp(self) self.cwd = os.getcwd() if not self.use_real_fs(): self.filesystem = fake_filesystem.FakeFilesystem( path_separator=self.path_separator()) self.open = fake_filesystem.FakeFileOpen(self.filesystem) self.os = fake_filesystem.FakeOsModule(self.filesystem) self.create_basepath() self.setUpFileSystem() def tearDown(self): RealFsTestMixin.tearDown(self) def setUpFileSystem(self): pass @property def is_windows_fs(self): if self.use_real_fs(): return self.is_windows return self.filesystem.is_windows_fs @property def is_macos(self): if self.use_real_fs(): return TestCase.is_macos return self.filesystem.is_macos pyfakefs-4.5.4/pyfakefs/tests/__init__.py0000666000000000000000000000000013757263357016561 0ustar 00000000000000pyfakefs-4.5.4/pyfakefs/_version.py0000666000000000000000000000002714167577652015517 0ustar 00000000000000__version__ = '4.5.4' pyfakefs-4.5.4/pyfakefs/__init__.py0000666000000000000000000000006113757263357015426 0ustar 00000000000000from ._version import __version__ # noqa: F401 pyfakefs-4.5.4/pyfakefs.egg-info/0000777000000000000000000000000014167600566015003 5ustar 00000000000000pyfakefs-4.5.4/pyfakefs.egg-info/dependency_links.txt0000666000000000000000000000000114167600565021050 0ustar 00000000000000 pyfakefs-4.5.4/pyfakefs.egg-info/entry_points.txt0000666000000000000000000000006314167600565020277 0ustar 00000000000000[pytest11] pytest_fakefs = pyfakefs.pytest_plugin pyfakefs-4.5.4/pyfakefs.egg-info/PKG-INFO0000666000000000000000000001743514167600565016111 0ustar 00000000000000Metadata-Version: 2.1 Name: pyfakefs Version: 4.5.4 Summary: pyfakefs implements a fake file system that mocks the Python file system modules. Home-page: http://pyfakefs.org Author: Google Author-email: google-pyfakefs@google.com Maintainer: John McGehee Maintainer-email: pyfakefs@johnnado.com License: http://www.apache.org/licenses/LICENSE-2.0 Description: # 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/jmcgeheeiv/pyfakefs/workflows/Testsuite/badge.svg) pyfakefs implements a fake file system that mocks the Python file system modules. Using pyfakefs, your tests operate on a fake file system in memory without touching the real disk. The software under test requires no modification to work with pyfakefs. pyfakefs works with Linux, Windows and MacOS. ## Documentation This file provides general usage instructions for pyfakefs. There is more: * The documentation at [GitHub Pages:](http://jmcgeheeiv.github.io/pyfakefs) * The [Release documentation](http://jmcgeheeiv.github.io/pyfakefs/release) contains usage documentation for pyfakefs and a description of the most relevant classes, methods and functions for the last version released on PyPi * The [Development documentation](http://jmcgeheeiv.github.io/pyfakefs/master) contains the same documentation for the current master branch * The [Release 3.7 documentation](http://jmcgeheeiv.github.io/pyfakefs/release37) contains usage documentation for the last version of pyfakefs supporting Python 2.7 * The [Release 3.3 documentation](http://jmcgeheeiv.github.io/pyfakefs/release33) contains usage documentation for the last version of pyfakefs supporting Python 2.6, and for the old-style API (which is still supported but not documented in the current release) * The [Release Notes](https://github.com/jmcgeheeiv/pyfakefs/blob/master/CHANGES.md) show a list of changes in the latest versions ### Linking to pyfakefs In your own documentation, please link to pyfakefs using the canonical URL . This URL always points to the most relevant top page for pyfakefs. ## Usage pyfakefs has support for `unittest` and `pytest`, but can also be used directly using `fake_filesystem_unittest.Patcher`. Refer to the [usage documentation](http://jmcgeheeiv.github.io/pyfakefs/master/usage.html) for more information on test scenarios, test customization and using convenience functions. ## Compatibility pyfakefs works with CPython 3.6 and above, on Linux, Windows and OSX (MacOS), and with PyPy3. pyfakefs works with [PyTest](http://doc.pytest.org) version 2.8.6 or above. pyfakefs will not work with Python libraries that use C libraries to access the file system. This is because pyfakefs cannot patch the underlying C libraries' file access functions--the C libraries will always access the real file system. For example, pyfakefs will not work with [`lxml`](http://lxml.de/). In this case `lxml` must be replaced with a pure Python alternative such as [`xml.etree.ElementTree`](https://docs.python.org/3/library/xml.etree.elementtree.html). ## Development ### Continuous integration pyfakefs is currently automatically tested on Linux, MacOS and Windows, with Python 3.6 to 3.10, and with PyPy3 on Linux, using [GitHub Actions](https://github.com/jmcgeheeiv/pyfakefs/actions). ### Running pyfakefs unit tests #### On the command line pyfakefs unit tests can be run using `unittest` or `pytest`: ```bash $ cd pyfakefs/ $ export PYTHONPATH=$PWD $ python -m pyfakefs.tests.all_tests $ python -m pyfakefs.tests.all_tests_without_extra_packages $ python -m pytest pyfakefs/pytest_tests/pytest_plugin_test.py ``` These scripts are called by `tox` and Travis-CI. `tox` can be used to run tests locally against supported python versions: ```bash $ tox ``` #### In a Docker container The `Dockerfile` at the repository root will run the tests on the latest Ubuntu version. Build the container: ```bash cd pyfakefs/ docker build -t pyfakefs . ``` Run the unit tests in the container: ```bash docker run -t pyfakefs ``` ### Contributing to pyfakefs We always welcome contributions to the library. Check out the [Contributing Guide](https://github.com/jmcgeheeiv/pyfakefs/blob/master/CONTRIBUTING.md) for more information. ## History pyfakefs.py was initially developed at Google by Mike Bland as a modest fake implementation of core Python modules. It was introduced to all of Google in September 2006. Since then, it has been enhanced to extend its functionality and usefulness. At last count, pyfakefs is used in over 2,000 Python tests at Google. Google released pyfakefs to the public in 2011 as Google Code project [pyfakefs](http://code.google.com/p/pyfakefs/): * Fork [jmcgeheeiv-pyfakefs](http://code.google.com/p/jmcgeheeiv-pyfakefs/) added [direct support for unittest and doctest](../../wiki/Automatically-find-and-patch-file-functions-and-modules) * Fork [shiffdane-jmcgeheeiv-pyfakefs](http://code.google.com/p/shiffdane-jmcgeheeiv-pyfakefs/) added further corrections After the [shutdown of Google Code](http://google-opensource.blogspot.com/2015/03/farewell-to-google-code.html) was announced, [John McGehee](https://github.com/jmcgeheeiv) merged all three Google Code projects together [here on GitHub](https://github.com/jmcgeheeiv/pyfakefs) where an enthusiastic community actively supports, maintains and extends pyfakefs. Keywords: testing,test,file,os,shutil,glob,mocking,unittest,fakes,filesystem,unit Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Console Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Operating System :: POSIX Classifier: Operating System :: MacOS Classifier: Operating System :: Microsoft :: Windows Classifier: Topic :: Software Development :: Libraries Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Topic :: Software Development :: Testing Classifier: Topic :: System :: Filesystems Classifier: Framework :: Pytest Requires-Python: >=3.6 Description-Content-Type: text/markdown pyfakefs-4.5.4/pyfakefs.egg-info/SOURCES.txt0000666000000000000000000000524014167600565016667 0ustar 00000000000000.gitignore CHANGES.md CONTRIBUTING.md COPYING Dockerfile MANIFEST.in README.md extra_requirements.txt mypy.ini requirements.txt setup.cfg setup.py tox.ini .github/ISSUE_TEMPLATE/bug_report.md .github/ISSUE_TEMPLATE/feature_request.md .github/workflows/builddocs.yml .github/workflows/dockertests.yml .github/workflows/pythonpackage.yml .github/workflows/run_pytest.sh .github/workflows/dockerfiles/Dockerfile_centos .github/workflows/dockerfiles/Dockerfile_debian .github/workflows/dockerfiles/Dockerfile_fedora .github/workflows/dockerfiles/Dockerfile_ubuntu docs/Makefile docs/api.rst docs/autopatch.rst docs/conf.py docs/index.rst docs/intro.rst docs/make.bat docs/modules.rst docs/usage.rst docs/pyfakefs_theme/theme.conf docs/pyfakefs_theme/static/pyfakefs.css pyfakefs/__init__.py pyfakefs/_version.py pyfakefs/deprecator.py pyfakefs/extra_packages.py pyfakefs/fake_filesystem.py pyfakefs/fake_filesystem_shutil.py pyfakefs/fake_filesystem_unittest.py pyfakefs/fake_pathlib.py pyfakefs/fake_scandir.py pyfakefs/helpers.py pyfakefs/mox3_stubout.py pyfakefs/patched_packages.py pyfakefs/pytest_plugin.py pyfakefs.egg-info/PKG-INFO pyfakefs.egg-info/SOURCES.txt pyfakefs.egg-info/dependency_links.txt pyfakefs.egg-info/entry_points.txt pyfakefs.egg-info/top_level.txt pyfakefs/pytest_tests/__init__.py pyfakefs/pytest_tests/conftest.py pyfakefs/pytest_tests/example.py pyfakefs/pytest_tests/pytest_check_failed_plugin_test.py pyfakefs/pytest_tests/pytest_doctest_test.py pyfakefs/pytest_tests/pytest_fixture_param_test.py pyfakefs/pytest_tests/pytest_fixture_test.py pyfakefs/pytest_tests/pytest_plugin_failing_helper.py pyfakefs/pytest_tests/pytest_plugin_test.py pyfakefs/tests/__init__.py pyfakefs/tests/all_tests.py pyfakefs/tests/all_tests_without_extra_packages.py pyfakefs/tests/dynamic_patch_test.py pyfakefs/tests/example.py pyfakefs/tests/example_test.py pyfakefs/tests/fake_filesystem_glob_test.py pyfakefs/tests/fake_filesystem_shutil_test.py pyfakefs/tests/fake_filesystem_test.py pyfakefs/tests/fake_filesystem_unittest_test.py pyfakefs/tests/fake_filesystem_vs_real_test.py pyfakefs/tests/fake_open_test.py pyfakefs/tests/fake_os_test.py pyfakefs/tests/fake_pathlib_test.py pyfakefs/tests/fake_stat_time_test.py pyfakefs/tests/fake_tempfile_test.py pyfakefs/tests/import_as_example.py pyfakefs/tests/logsio.py pyfakefs/tests/mox3_stubout_example.py pyfakefs/tests/mox3_stubout_test.py pyfakefs/tests/patched_packages_test.py pyfakefs/tests/performance_test.py pyfakefs/tests/test_utils.py pyfakefs/tests/fixtures/__init__.py pyfakefs/tests/fixtures/config_module.py pyfakefs/tests/fixtures/deprecated_property.py pyfakefs/tests/fixtures/excel_test.xlsx pyfakefs/tests/fixtures/module_with_attributes.pypyfakefs-4.5.4/pyfakefs.egg-info/top_level.txt0000666000000000000000000000001114167600565017524 0ustar 00000000000000pyfakefs pyfakefs-4.5.4/README.md0000666000000000000000000001240714147521400012747 0ustar 00000000000000# 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/jmcgeheeiv/pyfakefs/workflows/Testsuite/badge.svg) pyfakefs implements a fake file system that mocks the Python file system modules. Using pyfakefs, your tests operate on a fake file system in memory without touching the real disk. The software under test requires no modification to work with pyfakefs. pyfakefs works with Linux, Windows and MacOS. ## Documentation This file provides general usage instructions for pyfakefs. There is more: * The documentation at [GitHub Pages:](http://jmcgeheeiv.github.io/pyfakefs) * The [Release documentation](http://jmcgeheeiv.github.io/pyfakefs/release) contains usage documentation for pyfakefs and a description of the most relevant classes, methods and functions for the last version released on PyPi * The [Development documentation](http://jmcgeheeiv.github.io/pyfakefs/master) contains the same documentation for the current master branch * The [Release 3.7 documentation](http://jmcgeheeiv.github.io/pyfakefs/release37) contains usage documentation for the last version of pyfakefs supporting Python 2.7 * The [Release 3.3 documentation](http://jmcgeheeiv.github.io/pyfakefs/release33) contains usage documentation for the last version of pyfakefs supporting Python 2.6, and for the old-style API (which is still supported but not documented in the current release) * The [Release Notes](https://github.com/jmcgeheeiv/pyfakefs/blob/master/CHANGES.md) show a list of changes in the latest versions ### Linking to pyfakefs In your own documentation, please link to pyfakefs using the canonical URL . This URL always points to the most relevant top page for pyfakefs. ## Usage pyfakefs has support for `unittest` and `pytest`, but can also be used directly using `fake_filesystem_unittest.Patcher`. Refer to the [usage documentation](http://jmcgeheeiv.github.io/pyfakefs/master/usage.html) for more information on test scenarios, test customization and using convenience functions. ## Compatibility pyfakefs works with CPython 3.6 and above, on Linux, Windows and OSX (MacOS), and with PyPy3. pyfakefs works with [PyTest](http://doc.pytest.org) version 2.8.6 or above. pyfakefs will not work with Python libraries that use C libraries to access the file system. This is because pyfakefs cannot patch the underlying C libraries' file access functions--the C libraries will always access the real file system. For example, pyfakefs will not work with [`lxml`](http://lxml.de/). In this case `lxml` must be replaced with a pure Python alternative such as [`xml.etree.ElementTree`](https://docs.python.org/3/library/xml.etree.elementtree.html). ## Development ### Continuous integration pyfakefs is currently automatically tested on Linux, MacOS and Windows, with Python 3.6 to 3.10, and with PyPy3 on Linux, using [GitHub Actions](https://github.com/jmcgeheeiv/pyfakefs/actions). ### Running pyfakefs unit tests #### On the command line pyfakefs unit tests can be run using `unittest` or `pytest`: ```bash $ cd pyfakefs/ $ export PYTHONPATH=$PWD $ python -m pyfakefs.tests.all_tests $ python -m pyfakefs.tests.all_tests_without_extra_packages $ python -m pytest pyfakefs/pytest_tests/pytest_plugin_test.py ``` These scripts are called by `tox` and Travis-CI. `tox` can be used to run tests locally against supported python versions: ```bash $ tox ``` #### In a Docker container The `Dockerfile` at the repository root will run the tests on the latest Ubuntu version. Build the container: ```bash cd pyfakefs/ docker build -t pyfakefs . ``` Run the unit tests in the container: ```bash docker run -t pyfakefs ``` ### Contributing to pyfakefs We always welcome contributions to the library. Check out the [Contributing Guide](https://github.com/jmcgeheeiv/pyfakefs/blob/master/CONTRIBUTING.md) for more information. ## History pyfakefs.py was initially developed at Google by Mike Bland as a modest fake implementation of core Python modules. It was introduced to all of Google in September 2006. Since then, it has been enhanced to extend its functionality and usefulness. At last count, pyfakefs is used in over 2,000 Python tests at Google. Google released pyfakefs to the public in 2011 as Google Code project [pyfakefs](http://code.google.com/p/pyfakefs/): * Fork [jmcgeheeiv-pyfakefs](http://code.google.com/p/jmcgeheeiv-pyfakefs/) added [direct support for unittest and doctest](../../wiki/Automatically-find-and-patch-file-functions-and-modules) * Fork [shiffdane-jmcgeheeiv-pyfakefs](http://code.google.com/p/shiffdane-jmcgeheeiv-pyfakefs/) added further corrections After the [shutdown of Google Code](http://google-opensource.blogspot.com/2015/03/farewell-to-google-code.html) was announced, [John McGehee](https://github.com/jmcgeheeiv) merged all three Google Code projects together [here on GitHub](https://github.com/jmcgeheeiv/pyfakefs) where an enthusiastic community actively supports, maintains and extends pyfakefs. pyfakefs-4.5.4/requirements.txt0000666000000000000000000000001713756767653015001 0ustar 00000000000000pytest>=2.8.6 pyfakefs-4.5.4/setup.cfg0000666000000000000000000000016614167600566013325 0ustar 00000000000000[metadata] description_file = README.md [bdist_wheel] universal = 0 [egg_info] tag_build = tag_date = 0 pyfakefs-4.5.4/setup.py0000666000000000000000000000607714147521400013210 0ustar 00000000000000#! /usr/bin/env python # Copyright 2009 Google Inc. All Rights Reserved. # Copyright 2014 Altera Corporation. All Rights Reserved. # Copyright 2014-2018 John McGehee # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. import os from typing import List from setuptools import setup, find_packages from pyfakefs import __version__ NAME = 'pyfakefs' REQUIRES: List[str] = [] DESCRIPTION = ('pyfakefs implements a fake file system that mocks ' 'the Python file system modules.') URL = "http://pyfakefs.org" BASE_PATH = os.path.abspath(os.path.dirname(__file__)) with open(os.path.join(BASE_PATH, 'README.md')) as f: LONG_DESCRIPTION = f.read() CLASSIFIERS = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', "Programming Language :: Python :: 3", 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', 'Programming Language :: Python :: 3.10', 'Programming Language :: Python :: Implementation :: CPython', 'Programming Language :: Python :: Implementation :: PyPy', 'Operating System :: POSIX', 'Operating System :: MacOS', 'Operating System :: Microsoft :: Windows', 'Topic :: Software Development :: Libraries', 'Topic :: Software Development :: Libraries :: Python Modules', 'Topic :: Software Development :: Testing', 'Topic :: System :: Filesystems', 'Framework :: Pytest', ] AUTHOR = 'Google' AUTHOR_EMAIL = 'google-pyfakefs@google.com' MAINTAINER = 'John McGehee' MAINTAINER_EMAIL = 'pyfakefs@johnnado.com' KEYWORDS = ("testing test file os shutil glob mocking unittest " "fakes filesystem unit").split(' ') params = dict( name=NAME, entry_points={ 'pytest11': ['pytest_fakefs = pyfakefs.pytest_plugin'], }, version=__version__, install_requires=REQUIRES, # metadata for upload to PyPI author=AUTHOR, author_email=AUTHOR_EMAIL, maintainer=MAINTAINER, maintainer_email=MAINTAINER_EMAIL, license='http://www.apache.org/licenses/LICENSE-2.0', description=DESCRIPTION, long_description=LONG_DESCRIPTION, long_description_content_type='text/markdown', keywords=KEYWORDS, url=URL, classifiers=CLASSIFIERS, python_requires='>=3.6', test_suite='pyfakefs.tests', packages=find_packages(exclude=['docs']) ) setup(**params) pyfakefs-4.5.4/tox.ini0000666000000000000000000000050413757263357013022 0ustar 00000000000000[tox] envlist=py35,py36,py37,py38,pypy [testenv] deps = -rrequirements.txt -rextra_requirements.txt passenv = HOME USERPROFILE commands= python -m pyfakefs.tests.all_tests python -m pyfakefs.tests.all_tests_without_extra_packages python -m pytest pyfakefs/pytest_tests/pytest_plugin_test.py