pax_global_header00006660000000000000000000000064142620740010014506gustar00rootroot0000000000000052 comment=e05d8c985a10176ad43eefb9c4bfa6d7ab176719 python-atomicwrites-1.4.1/000077500000000000000000000000001426207400100155425ustar00rootroot00000000000000python-atomicwrites-1.4.1/.gitignore000066400000000000000000000001241426207400100175270ustar00rootroot00000000000000.tox *.pyc *.pyo __pycache__ *.egg-info docs/_build build dist .cache .pytest_cache python-atomicwrites-1.4.1/.travis.yml000066400000000000000000000011561426207400100176560ustar00rootroot00000000000000os: linux language: python arch: - amd64 - ppc64le python: - 2.7 - pypy - 3.4 - 3.5 - 3.6 - 3.7 - 3.8 env: - TOXENV=test matrix: include: - python: 2.7 env: TOXENV=stylecheck - python: 3.6 env: TOXENV=stylecheck #Adding power support architecture - python: 2.7 env: TOXENV=stylecheck arch: ppc64le - python: 3.6 env: TOXENV=stylecheck arch: ppc64le exclude: - python: pypy arch: ppc64le install: - pip install tox script: - tox python-atomicwrites-1.4.1/CONTRIBUTING.rst000066400000000000000000000003721426207400100202050ustar00rootroot00000000000000Thanks for contributing to python-atomicwrites! This document is a work-in-progress. Below are a few notes that are useful for writing patches. Running the tests ================= :: pip install tox tox -e py-test tox -e py-stylecheck python-atomicwrites-1.4.1/LICENSE000066400000000000000000000020551426207400100165510ustar00rootroot00000000000000Copyright (c) 2015-2016 Markus Unterwaditzer Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. python-atomicwrites-1.4.1/MANIFEST.in000066400000000000000000000002321426207400100172750ustar00rootroot00000000000000include LICENSE include README.rst recursive-include docs * recursive-include tests * prune docs/_build global-exclude *.py[cdo] __pycache__ *.so *.pyd python-atomicwrites-1.4.1/Makefile000066400000000000000000000001011426207400100171720ustar00rootroot00000000000000release: python setup.py sdist bdist_wheel twine upload dist/* python-atomicwrites-1.4.1/README.rst000066400000000000000000000117261426207400100172400ustar00rootroot00000000000000=================== python-atomicwrites =================== .. image:: https://travis-ci.com/untitaker/python-atomicwrites.svg?branch=master :target: https://travis-ci.com/untitaker/python-atomicwrites .. image:: https://ci.appveyor.com/api/projects/status/vadc4le3c27to59x/branch/master?svg=true :target: https://ci.appveyor.com/project/untitaker/python-atomicwrites/branch/master .. image:: https://readthedocs.org/projects/python-atomicwrites/badge/?version=latest :target: https://python-atomicwrites.readthedocs.io/en/latest/?badge=latest :alt: Documentation Status **Atomic file writes.** .. code-block:: python from atomicwrites import atomic_write with atomic_write('foo.txt', overwrite=True) as f: f.write('Hello world.') # "foo.txt" doesn't exist yet. # Now it does. See `API documentation `_ for more low-level interfaces. Features that distinguish it from other similar libraries (see `Alternatives and Credit`_): - Race-free assertion that the target file doesn't yet exist. This can be controlled with the ``overwrite`` parameter. - Windows support, although not well-tested. The MSDN resources are not very explicit about which operations are atomic. I'm basing my assumptions off `a comment `_ by `Doug Cook `_, who appears to be a Microsoft employee: Question: Is MoveFileEx atomic if the existing and new files are both on the same drive? The simple answer is "usually, but in some cases it will silently fall-back to a non-atomic method, so don't count on it". The implementation of MoveFileEx looks something like this: [...] The problem is if the rename fails, you might end up with a CopyFile, which is definitely not atomic. If you really need atomic-or-nothing, you can try calling NtSetInformationFile, which is unsupported but is much more likely to be atomic. - Simple high-level API that wraps a very flexible class-based API. - Consistent error handling across platforms. How it works ============ It uses a temporary file in the same directory as the given path. This ensures that the temporary file resides on the same filesystem. The temporary file will then be atomically moved to the target location: On POSIX, it will use ``rename`` if files should be overwritten, otherwise a combination of ``link`` and ``unlink``. On Windows, it uses MoveFileEx_ through stdlib's ``ctypes`` with the appropriate flags. Note that with ``link`` and ``unlink``, there's a timewindow where the file might be available under two entries in the filesystem: The name of the temporary file, and the name of the target file. Also note that the permissions of the target file may change this way. In some situations a ``chmod`` can be issued without any concurrency problems, but since that is not always the case, this library doesn't do it by itself. .. _MoveFileEx: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365240%28v=vs.85%29.aspx fsync ----- On POSIX, ``fsync`` is invoked on the temporary file after it is written (to flush file content and metadata), and on the parent directory after the file is moved (to flush filename). ``fsync`` does not take care of disks' internal buffers, but there don't seem to be any standard POSIX APIs for that. On OS X, ``fcntl`` is used with ``F_FULLFSYNC`` instead of ``fsync`` for that reason. On Windows, `_commit `_ is used, but there are no guarantees about disk internal buffers. Alternatives and Credit ======================= Atomicwrites is directly inspired by the following libraries (and shares a minimal amount of code): - The Trac project's `utility functions `_, also used in `Werkzeug `_ and `mitsuhiko/python-atomicfile `_. The idea to use ``ctypes`` instead of ``PyWin32`` originated there. - `abarnert/fatomic `_. Windows support (based on ``PyWin32``) was originally taken from there. Other alternatives to atomicwrites include: - `sashka/atomicfile `_. Originally I considered using that, but at the time it was lacking a lot of features I needed (Windows support, overwrite-parameter, overriding behavior through subclassing). - The `Boltons library collection `_ features a class for atomic file writes, which seems to have a very similar ``overwrite`` parameter. It is lacking Windows support though. License ======= Licensed under the MIT, see ``LICENSE``. python-atomicwrites-1.4.1/appveyor.yml000066400000000000000000000013701426207400100201330ustar00rootroot00000000000000build: false # Not a C# project, build stuff at the test step instead. environment: matrix: - PYTHON: "C:/Python27" - PYTHON: "C:/Python27-x64" - PYTHON: "C:/Python34" - PYTHON: "C:/Python34-x64" - PYTHON: "C:/Python35" - PYTHON: "C:/Python35-x64" - PYTHON: "C:/Python36" - PYTHON: "C:/Python36-x64" - PYTHON: "C:/Python37" - PYTHON: "C:/Python37-x64" - PYTHON: "C:/Python38" - PYTHON: "C:/Python38-x64" init: - "ECHO %PYTHON%" - ps: "ls C:/Python*" install: - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py') - "%PYTHON%/python.exe C:/get-pip.py" - "%PYTHON%/Scripts/pip.exe install tox" test_script: - "%PYTHON%/Scripts/tox.exe -e py-test" python-atomicwrites-1.4.1/atomicwrites/000077500000000000000000000000001426207400100202545ustar00rootroot00000000000000python-atomicwrites-1.4.1/atomicwrites/__init__.py000066400000000000000000000154721426207400100223760ustar00rootroot00000000000000import contextlib import io import os import sys import tempfile try: import fcntl except ImportError: fcntl = None # `fspath` was added in Python 3.6 try: from os import fspath except ImportError: fspath = None __version__ = '1.4.1' PY2 = sys.version_info[0] == 2 text_type = unicode if PY2 else str # noqa def _path_to_unicode(x): if not isinstance(x, text_type): return x.decode(sys.getfilesystemencoding()) return x DEFAULT_MODE = "wb" if PY2 else "w" _proper_fsync = os.fsync if sys.platform != 'win32': if hasattr(fcntl, 'F_FULLFSYNC'): def _proper_fsync(fd): # https://lists.apple.com/archives/darwin-dev/2005/Feb/msg00072.html # https://developer.apple.com/library/mac/documentation/Darwin/Reference/ManPages/man2/fsync.2.html # https://github.com/untitaker/python-atomicwrites/issues/6 fcntl.fcntl(fd, fcntl.F_FULLFSYNC) def _sync_directory(directory): # Ensure that filenames are written to disk fd = os.open(directory, 0) try: _proper_fsync(fd) finally: os.close(fd) def _replace_atomic(src, dst): os.rename(src, dst) _sync_directory(os.path.normpath(os.path.dirname(dst))) def _move_atomic(src, dst): os.link(src, dst) os.unlink(src) src_dir = os.path.normpath(os.path.dirname(src)) dst_dir = os.path.normpath(os.path.dirname(dst)) _sync_directory(dst_dir) if src_dir != dst_dir: _sync_directory(src_dir) else: from ctypes import windll, WinError _MOVEFILE_REPLACE_EXISTING = 0x1 _MOVEFILE_WRITE_THROUGH = 0x8 _windows_default_flags = _MOVEFILE_WRITE_THROUGH def _handle_errors(rv): if not rv: raise WinError() def _replace_atomic(src, dst): _handle_errors(windll.kernel32.MoveFileExW( _path_to_unicode(src), _path_to_unicode(dst), _windows_default_flags | _MOVEFILE_REPLACE_EXISTING )) def _move_atomic(src, dst): _handle_errors(windll.kernel32.MoveFileExW( _path_to_unicode(src), _path_to_unicode(dst), _windows_default_flags )) def replace_atomic(src, dst): ''' Move ``src`` to ``dst``. If ``dst`` exists, it will be silently overwritten. Both paths must reside on the same filesystem for the operation to be atomic. ''' return _replace_atomic(src, dst) def move_atomic(src, dst): ''' Move ``src`` to ``dst``. There might a timewindow where both filesystem entries exist. If ``dst`` already exists, :py:exc:`FileExistsError` will be raised. Both paths must reside on the same filesystem for the operation to be atomic. ''' return _move_atomic(src, dst) class AtomicWriter(object): ''' A helper class for performing atomic writes. Usage:: with AtomicWriter(path).open() as f: f.write(...) :param path: The destination filepath. May or may not exist. :param mode: The filemode for the temporary file. This defaults to `wb` in Python 2 and `w` in Python 3. :param overwrite: If set to false, an error is raised if ``path`` exists. Errors are only raised after the file has been written to. Either way, the operation is atomic. :param open_kwargs: Keyword-arguments to pass to the underlying :py:func:`open` call. This can be used to set the encoding when opening files in text-mode. If you need further control over the exact behavior, you are encouraged to subclass. ''' def __init__(self, path, mode=DEFAULT_MODE, overwrite=False, **open_kwargs): if 'a' in mode: raise ValueError( 'Appending to an existing file is not supported, because that ' 'would involve an expensive `copy`-operation to a temporary ' 'file. Open the file in normal `w`-mode and copy explicitly ' 'if that\'s what you\'re after.' ) if 'x' in mode: raise ValueError('Use the `overwrite`-parameter instead.') if 'w' not in mode: raise ValueError('AtomicWriters can only be written to.') # Attempt to convert `path` to `str` or `bytes` if fspath is not None: path = fspath(path) self._path = path self._mode = mode self._overwrite = overwrite self._open_kwargs = open_kwargs def open(self): ''' Open the temporary file. ''' return self._open(self.get_fileobject) @contextlib.contextmanager def _open(self, get_fileobject): f = None # make sure f exists even if get_fileobject() fails try: success = False with get_fileobject(**self._open_kwargs) as f: yield f self.sync(f) self.commit(f) success = True finally: if not success: try: self.rollback(f) except Exception: pass def get_fileobject(self, suffix="", prefix=tempfile.gettempprefix(), dir=None, **kwargs): '''Return the temporary file to use.''' if dir is None: dir = os.path.normpath(os.path.dirname(self._path)) descriptor, name = tempfile.mkstemp(suffix=suffix, prefix=prefix, dir=dir) # io.open() will take either the descriptor or the name, but we need # the name later for commit()/replace_atomic() and couldn't find a way # to get the filename from the descriptor. os.close(descriptor) kwargs['mode'] = self._mode kwargs['file'] = name return io.open(**kwargs) def sync(self, f): '''responsible for clearing as many file caches as possible before commit''' f.flush() _proper_fsync(f.fileno()) def commit(self, f): '''Move the temporary file to the target location.''' if self._overwrite: replace_atomic(f.name, self._path) else: move_atomic(f.name, self._path) def rollback(self, f): '''Clean up all temporary resources.''' os.unlink(f.name) def atomic_write(path, writer_cls=AtomicWriter, **cls_kwargs): ''' Simple atomic writes. This wraps :py:class:`AtomicWriter`:: with atomic_write(path) as f: f.write(...) :param path: The target path to write to. :param writer_cls: The writer class to use. This parameter is useful if you subclassed :py:class:`AtomicWriter` to change some behavior and want to use that new subclass. Additional keyword arguments are passed to the writer class. See :py:class:`AtomicWriter`. ''' return writer_cls(path, **cls_kwargs).open() python-atomicwrites-1.4.1/docs/000077500000000000000000000000001426207400100164725ustar00rootroot00000000000000python-atomicwrites-1.4.1/docs/Makefile000066400000000000000000000152021426207400100201320ustar00rootroot00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # 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 clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext 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 " 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)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." 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." 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/atomicwrites.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/atomicwrites.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/atomicwrites" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/atomicwrites" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." 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)." 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." 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." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." 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)." 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." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." 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." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." python-atomicwrites-1.4.1/docs/conf.py000066400000000000000000000063331426207400100177760ustar00rootroot00000000000000#!/usr/bin/env python import os import sys import pkg_resources extensions = [ 'sphinx.ext.autodoc', 'sphinx.ext.intersphinx', 'sphinx.ext.viewcode', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix of source filenames. source_suffix = '.rst' # The master toctree document. master_doc = 'index' # General information about the project. project = 'atomicwrites' copyright = '2015-2017, Markus Unterwaditzer' try: # The full version, including alpha/beta/rc tags. release = pkg_resources.require('atomicwrites')[0].version except pkg_resources.DistributionNotFound: print('To build the documentation, the distribution information of ' 'atomicwrites has to be available. Run "setup.py develop" to do ' 'this.') sys.exit(1) version = '.'.join(release.split('.')[:2]) # The short X.Y version. on_rtd = os.environ.get('READTHEDOCS', None) == 'True' try: import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] except ImportError: html_theme = 'default' if not on_rtd: print('-' * 74) print('Warning: sphinx-rtd-theme not installed, building with default ' 'theme.') print('-' * 74) # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = ['_build'] # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # 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 = ['_static'] # Output file base name for HTML help builder. htmlhelp_basename = 'atomicwritesdoc' # -- Options for LaTeX output --------------------------------------------- latex_elements = {} # 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 = [ ('index', 'atomicwrites.tex', 'atomicwrites Documentation', 'Markus Unterwaditzer', 'manual'), ] # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ ('index', 'atomicwrites', 'atomicwrites Documentation', ['Markus Unterwaditzer'], 1) ] # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ ('index', 'atomicwrites', 'atomicwrites Documentation', 'Markus Unterwaditzer', 'atomicwrites', 'One line description of project.', 'Miscellaneous'), ] # Bibliographic Dublin Core info. epub_title = 'atomicwrites' epub_author = 'Markus Unterwaditzer' epub_publisher = 'Markus Unterwaditzer' epub_copyright = '2015, Markus Unterwaditzer' # A list of files that should not be packed into the epub file. epub_exclude_files = ['search.html'] # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {'http://docs.python.org/': None} python-atomicwrites-1.4.1/docs/index.rst000066400000000000000000000011371426207400100203350ustar00rootroot00000000000000.. include:: ../README.rst .. module:: atomicwrites API === .. autofunction:: atomic_write Errorhandling ------------- All filesystem errors are subclasses of :py:exc:`OSError`. - On UNIX systems, errors from the Python stdlib calls are thrown. - On Windows systems, errors from Python's ``ctypes`` are thrown. In either case, the ``errno`` attribute on the thrown exception maps to an errorcode in the ``errno`` module. Low-level API ------------- .. autofunction:: replace_atomic .. autofunction:: move_atomic .. autoclass:: AtomicWriter :members: License ======= .. include:: ../LICENSE python-atomicwrites-1.4.1/docs/make.bat000066400000000000000000000150711426207400100201030ustar00rootroot00000000000000@ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build 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. 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 goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %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 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. 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\atomicwrites.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\atomicwrites.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" == "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 %BUILDDIR%/.. 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 %BUILDDIR%/.. 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" == "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 ) :end python-atomicwrites-1.4.1/setup.cfg000066400000000000000000000000261426207400100173610ustar00rootroot00000000000000[wheel] universal = 1 python-atomicwrites-1.4.1/setup.py000066400000000000000000000024071426207400100172570ustar00rootroot00000000000000# -*- coding: utf-8 -*- import ast import re from setuptools import find_packages, setup _version_re = re.compile(r'__version__\s+=\s+(.*)') with open('atomicwrites/__init__.py', 'rb') as f: version = str(ast.literal_eval(_version_re.search( f.read().decode('utf-8')).group(1))) setup( name='atomicwrites', version=version, author='Markus Unterwaditzer', author_email='markus@unterwaditzer.net', url='https://github.com/untitaker/python-atomicwrites', description='Atomic file writes.', license='MIT', long_description=open('README.rst').read(), packages=find_packages(exclude=['tests.*', 'tests']), include_package_data=True, classifiers=[ 'License :: OSI Approved :: MIT License', 'Programming Language :: Python :: 2', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6', 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: Implementation :: CPython', ], python_requires='>=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*', ) python-atomicwrites-1.4.1/tests/000077500000000000000000000000001426207400100167045ustar00rootroot00000000000000python-atomicwrites-1.4.1/tests/test_atomicwrites.py000066400000000000000000000052511426207400100230320ustar00rootroot00000000000000import errno import os from atomicwrites import atomic_write import pytest def test_atomic_write(tmpdir): fname = tmpdir.join('ha') for i in range(2): with atomic_write(str(fname), overwrite=True) as f: f.write('hoho') with pytest.raises(OSError) as excinfo: with atomic_write(str(fname), overwrite=False) as f: f.write('haha') assert excinfo.value.errno == errno.EEXIST assert fname.read() == 'hoho' assert len(tmpdir.listdir()) == 1 def test_teardown(tmpdir): fname = tmpdir.join('ha') with pytest.raises(AssertionError): with atomic_write(str(fname), overwrite=True): assert False assert not tmpdir.listdir() def test_replace_simultaneously_created_file(tmpdir): fname = tmpdir.join('ha') with atomic_write(str(fname), overwrite=True) as f: f.write('hoho') fname.write('harhar') assert fname.read() == 'harhar' assert fname.read() == 'hoho' assert len(tmpdir.listdir()) == 1 def test_dont_remove_simultaneously_created_file(tmpdir): fname = tmpdir.join('ha') with pytest.raises(OSError) as excinfo: with atomic_write(str(fname), overwrite=False) as f: f.write('hoho') fname.write('harhar') assert fname.read() == 'harhar' assert excinfo.value.errno == errno.EEXIST assert fname.read() == 'harhar' assert len(tmpdir.listdir()) == 1 # Verify that nested exceptions during rollback do not overwrite the initial # exception that triggered a rollback. def test_open_reraise(tmpdir): fname = tmpdir.join('ha') with pytest.raises(AssertionError): aw = atomic_write(str(fname), overwrite=False) with aw: # Mess with internals, so commit will trigger a ValueError. We're # testing that the initial AssertionError triggered below is # propagated up the stack, not the second exception triggered # during commit. aw.rollback = lambda: 1 / 0 # Now trigger our own exception. assert False, "Intentional failure for testing purposes" def test_atomic_write_in_pwd(tmpdir): orig_curdir = os.getcwd() try: os.chdir(str(tmpdir)) fname = 'ha' for i in range(2): with atomic_write(str(fname), overwrite=True) as f: f.write('hoho') with pytest.raises(OSError) as excinfo: with atomic_write(str(fname), overwrite=False) as f: f.write('haha') assert excinfo.value.errno == errno.EEXIST assert open(fname).read() == 'hoho' assert len(tmpdir.listdir()) == 1 finally: os.chdir(orig_curdir) python-atomicwrites-1.4.1/tox.ini000066400000000000000000000003231426207400100170530ustar00rootroot00000000000000[tox] envlist = py{27,py,34,35,36,37,38}-{test,stylecheck} [testenv] deps = test: pytest stylecheck: flake8 stylecheck: flake8-import-order commands = test: py.test [] stylecheck: flake8 []