safer


Namesafer JSON
Version 4.12.3 PyPI version JSON
download
home_pagehttps://github.com/rec/safer
Summary🧿 A safer writer for files and streams 🧿
upload_time2024-01-15 10:21:50
maintainer
docs_urlNone
authorTom Ritchford
requires_python>=3.8
licenseMIT
keywords
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI
coveralls test coverage No coveralls.
            # 🧿 `safer`: A safer writer 🧿

Avoid partial writes or corruption!

`safer` wraps file streams, sockets, or a callable, and offers a drop-in
replacement for regular old `open()`.

## Quick summary

### A tiny example

    import safer

    with safer.open(filename, 'w') as fp:
        fp.write('one')
        print('two', file=fp)
        raise ValueError
        # filename was not written.


### How to use

Use [pip](https://pypi.org/project/pip) to install `safer` from the command
line: `pip install safer`.

Tested on Python 3.4 - 3.11.  An old Python 2.7 version
is [here](https://github.com/rec/safer/tree/v2.0.5).

See the Medium article [here](https://medium.com/@TomSwirly/%EF%B8%8F-safer-a-safer-file-writer-%EF%B8%8F-5fe267dbe3f5)

### The details

`safer` helps prevent programmer error from corrupting files, socket
connections, or generalized streams by writing a whole file or nothing.

It does not prevent concurrent modification of files from other threads or
processes: if you need atomic file writing, see
https://pypi.org/project/atomicwrites/

It also has a useful `dry_run` setting to let you test your code without
actually overwriting the target file.

* `safer.writer()` wraps an existing writer, socket or stream and writes a
  whole response or nothing

* `safer.open()` is a drop-in replacement for built-in `open` that
  writes a whole file or nothing

* `safer.closer()` returns a stream like from `safer.write()` that also
  closes the underlying stream or callable when it closes.

* `safer.dump()` is like a safer `json.dump()` which can be used for any
  serialization protocol, including Yaml and Toml, and also allows you to
  write to file streams or any other callable.

* `safer.printer()` is `safer.open()` except that it yields a
  a function that prints to the stream.

By default, `safer` buffers the written data in memory in a `io.StringIO`
or `io.BytesIO`.

For very large files, `safer.open()` has a `temp_file` argument which
writes the data to a temporary file on disk, which is moved over using
`os.rename` if the operation completes successfully.  This functionality
does not work on Windows.  (In fact, it's unclear if any of this works on
Windows, but that certainly won't.  Windows developer solicted!)


### Example: `safer.writer()`

`safer.writer()` wraps an existing stream - a writer, socket, or callback -
in a temporary stream which is only copied to the target stream at close(), and
only if no exception was raised.

Suppose `sock = socket.socket(*args)`.

The old, dangerous way goes like this.

    try:
        write_header(sock)
        write_body(sock)   # Exception is thrown here
        write_footer(sock)
     except Exception:
        write_error(sock)  # Oops, the header was already written

With `safer` you write all or nothing:

    try:
        with safer.writer(sock) as s:
            write_header(s)
            write_body(s)  # Exception is thrown here
            write_footer(s)
     except Exception:
        write_error(sock)  # Nothing has been written

### Example: `safer.open()` and json

`safer.open()` is a a drop-in replacement for built-in `open()` except that
when used as a context, it leaves the original file unchanged on failure.

It's easy to write broken JSON if something within it doesn't serialize.

    with open(filename, 'w') as fp:
        json.dump(data, fp)
        # If an exception is raised, the file is empty or partly written

`safer` prevents this:

    with safer.open(filename, 'w') as fp:
        json.dump(data, fp)
        # If an exception is raised, the file is unchanged.

`safer.open(filename)` returns a file stream `fp` like `open(filename)`
would, except that `fp` writes to memory stream or a temporary file in the
same directory.

If `fp` is used as a context manager and an exception is raised, then the
property `fp.safer_failed` on the stream is automatically set to `True`.

And when `fp.close()` is called, the cached data is stored in `filename` -
*unless* `fp.safer_failed` is true.

### Example: `safer.printer()`

`safer.printer()` is similar to `safer.open()` except it yields a function
that prints to the open file - it's very convenient for printing text.

Like `safer.open()`, if an exception is raised within its context manager,
the original file is left unchanged.

Before.

    with open(file, 'w') as fp:
        for item in items:
            print(item, file=fp)
        # Prints lines until the first exception

With `safer`

    with safer.printer(file) as print:
        for item in items:
            print(item)
        # Either the whole file is written, or nothing


### [API Documentation](https://rec.github.io/safer#safer--api-documentation)

            

Raw data

            {
    "_id": null,
    "home_page": "https://github.com/rec/safer",
    "name": "safer",
    "maintainer": "",
    "docs_url": null,
    "requires_python": ">=3.8",
    "maintainer_email": "",
    "keywords": "",
    "author": "Tom Ritchford",
    "author_email": "tom@swirly.com",
    "download_url": "https://files.pythonhosted.org/packages/ce/2a/c3335267939708bbf8d6a9bb7334b88057bdafd2a2234fbe38de169578f2/safer-4.12.3.tar.gz",
    "platform": null,
    "description": "# \ud83e\uddff `safer`: A safer writer \ud83e\uddff\n\nAvoid partial writes or corruption!\n\n`safer` wraps file streams, sockets, or a callable, and offers a drop-in\nreplacement for regular old `open()`.\n\n## Quick summary\n\n### A tiny example\n\n    import safer\n\n    with safer.open(filename, 'w') as fp:\n        fp.write('one')\n        print('two', file=fp)\n        raise ValueError\n        # filename was not written.\n\n\n### How to use\n\nUse [pip](https://pypi.org/project/pip) to install `safer` from the command\nline: `pip install safer`.\n\nTested on Python 3.4 - 3.11.  An old Python 2.7 version\nis [here](https://github.com/rec/safer/tree/v2.0.5).\n\nSee the Medium article [here](https://medium.com/@TomSwirly/%EF%B8%8F-safer-a-safer-file-writer-%EF%B8%8F-5fe267dbe3f5)\n\n### The details\n\n`safer` helps prevent programmer error from corrupting files, socket\nconnections, or generalized streams by writing a whole file or nothing.\n\nIt does not prevent concurrent modification of files from other threads or\nprocesses: if you need atomic file writing, see\nhttps://pypi.org/project/atomicwrites/\n\nIt also has a useful `dry_run` setting to let you test your code without\nactually overwriting the target file.\n\n* `safer.writer()` wraps an existing writer, socket or stream and writes a\n  whole response or nothing\n\n* `safer.open()` is a drop-in replacement for built-in `open` that\n  writes a whole file or nothing\n\n* `safer.closer()` returns a stream like from `safer.write()` that also\n  closes the underlying stream or callable when it closes.\n\n* `safer.dump()` is like a safer `json.dump()` which can be used for any\n  serialization protocol, including Yaml and Toml, and also allows you to\n  write to file streams or any other callable.\n\n* `safer.printer()` is `safer.open()` except that it yields a\n  a function that prints to the stream.\n\nBy default, `safer` buffers the written data in memory in a `io.StringIO`\nor `io.BytesIO`.\n\nFor very large files, `safer.open()` has a `temp_file` argument which\nwrites the data to a temporary file on disk, which is moved over using\n`os.rename` if the operation completes successfully.  This functionality\ndoes not work on Windows.  (In fact, it's unclear if any of this works on\nWindows, but that certainly won't.  Windows developer solicted!)\n\n\n### Example: `safer.writer()`\n\n`safer.writer()` wraps an existing stream - a writer, socket, or callback -\nin a temporary stream which is only copied to the target stream at close(), and\nonly if no exception was raised.\n\nSuppose `sock = socket.socket(*args)`.\n\nThe old, dangerous way goes like this.\n\n    try:\n        write_header(sock)\n        write_body(sock)   # Exception is thrown here\n        write_footer(sock)\n     except Exception:\n        write_error(sock)  # Oops, the header was already written\n\nWith `safer` you write all or nothing:\n\n    try:\n        with safer.writer(sock) as s:\n            write_header(s)\n            write_body(s)  # Exception is thrown here\n            write_footer(s)\n     except Exception:\n        write_error(sock)  # Nothing has been written\n\n### Example: `safer.open()` and json\n\n`safer.open()` is a a drop-in replacement for built-in `open()` except that\nwhen used as a context, it leaves the original file unchanged on failure.\n\nIt's easy to write broken JSON if something within it doesn't serialize.\n\n    with open(filename, 'w') as fp:\n        json.dump(data, fp)\n        # If an exception is raised, the file is empty or partly written\n\n`safer` prevents this:\n\n    with safer.open(filename, 'w') as fp:\n        json.dump(data, fp)\n        # If an exception is raised, the file is unchanged.\n\n`safer.open(filename)` returns a file stream `fp` like `open(filename)`\nwould, except that `fp` writes to memory stream or a temporary file in the\nsame directory.\n\nIf `fp` is used as a context manager and an exception is raised, then the\nproperty `fp.safer_failed` on the stream is automatically set to `True`.\n\nAnd when `fp.close()` is called, the cached data is stored in `filename` -\n*unless* `fp.safer_failed` is true.\n\n### Example: `safer.printer()`\n\n`safer.printer()` is similar to `safer.open()` except it yields a function\nthat prints to the open file - it's very convenient for printing text.\n\nLike `safer.open()`, if an exception is raised within its context manager,\nthe original file is left unchanged.\n\nBefore.\n\n    with open(file, 'w') as fp:\n        for item in items:\n            print(item, file=fp)\n        # Prints lines until the first exception\n\nWith `safer`\n\n    with safer.printer(file) as print:\n        for item in items:\n            print(item)\n        # Either the whole file is written, or nothing\n\n\n### [API Documentation](https://rec.github.io/safer#safer--api-documentation)\n",
    "bugtrack_url": null,
    "license": "MIT",
    "summary": "\ud83e\uddff A safer writer for files and streams \ud83e\uddff",
    "version": "4.12.3",
    "project_urls": {
        "Documentation": "https://rec.github.io/safer",
        "Homepage": "https://github.com/rec/safer",
        "Repository": "https://github.com/rec/safer"
    },
    "split_keywords": [],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "a90ed6ebd4a5594750ed5ab06e76e9d652bc1058fc4b6bc7898d81d3ab04d0ea",
                "md5": "2b1cbef9ac9e6cade4e1627437fe3cdd",
                "sha256": "def5100a9eac29379ef7c8fac5f0232b80fbcb7915e1dcfcd35df1a282696663"
            },
            "downloads": -1,
            "filename": "safer-4.12.3-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "2b1cbef9ac9e6cade4e1627437fe3cdd",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.8",
            "size": 10504,
            "upload_time": "2024-01-15T10:21:49",
            "upload_time_iso_8601": "2024-01-15T10:21:49.527130Z",
            "url": "https://files.pythonhosted.org/packages/a9/0e/d6ebd4a5594750ed5ab06e76e9d652bc1058fc4b6bc7898d81d3ab04d0ea/safer-4.12.3-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "ce2ac3335267939708bbf8d6a9bb7334b88057bdafd2a2234fbe38de169578f2",
                "md5": "192fab3d6478fd54b2359deb56ea74ff",
                "sha256": "264eb65b2c4efa324099054015a07917cd7b31ae03f8f04471696b474890c9c1"
            },
            "downloads": -1,
            "filename": "safer-4.12.3.tar.gz",
            "has_sig": false,
            "md5_digest": "192fab3d6478fd54b2359deb56ea74ff",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.8",
            "size": 8515,
            "upload_time": "2024-01-15T10:21:50",
            "upload_time_iso_8601": "2024-01-15T10:21:50.869598Z",
            "url": "https://files.pythonhosted.org/packages/ce/2a/c3335267939708bbf8d6a9bb7334b88057bdafd2a2234fbe38de169578f2/safer-4.12.3.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-01-15 10:21:50",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "rec",
    "github_project": "safer",
    "travis_ci": true,
    "coveralls": false,
    "github_actions": true,
    "tox": true,
    "lcname": "safer"
}
        
Elapsed time: 0.16289s