diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..755a5bc --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,33 @@ +name: release + +on: + release: + types: [published] + +permissions: + contents: read + +jobs: + pypi-publish: + name: Upload release to PyPI + runs-on: ubuntu-latest + environment: + name: pypi + url: https://pypi.org/project/network_file_system + steps: + - uses: actions/checkout@v3 + - name: Set up Python + uses: actions/setup-python@v3 + with: + python-version: '3.x' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install build + - name: Build package + run: python -m build + - name: Publish package distributions to PyPI + uses: pypa/gh-action-pypi-publish@release/v1 + with: + user: __token__ + password: ${{ secrets.PYPI_API }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..69daaec --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,27 @@ +name: test + +on: [push] + +jobs: + build: + + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.7"] + + steps: + - uses: actions/checkout@v3 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install pytest + python -m pip install -r requirements.txt + python -m pip install . + - name: Test with pytest + run: | + pytest . \ No newline at end of file diff --git a/README.rst b/README.rst index e80c4c6..542e133 100644 --- a/README.rst +++ b/README.rst @@ -1,12 +1,100 @@ -Network-File-System -=================== -|BlackStyle| +#################### +rename_after_writing +#################### +|TestStatus| |PyPiStatus| |BlackStyle| |PackStyleBlack| + +A python-library to help with the completeness of files when moving, copying and writing. +Writing a file is not atomic. In the worst case the process writing the file +dies while the file is not yet complete. +Also in some network-file-systems, the destination of a ``move`` or ``copy`` might show up before it is complete. +Incomplete files are a potential risk for the integrity of your data. +In the best case your reading process crashes, but in the worst case the incompleteness goes unnoticed. +This package reduces the risk of having incomplete files by making all writing +operations write to a temporary file in the destination's directory first before renaming it to its final destination. +On most file-systems this final renamig is atomic. + + +******* +Install +******* + +.. code-block:: + + pip install network_file_system + + +********* +Functions +********* + +open +==== + +Just like python's built in ``open()`` but when writing (``mode=w``) everything is writen +to a temporary file in the destination's directory before the temporary file is renamed to the final destination. + +.. code-block:: python + + import network_file_system as nfs + + with nfs.open("my_file.txt", "wt") as f: + for i in range(10): + f.write(input()) + + +copy +==== + +Copies the file first to a temporary file in the destinations directory +before moving it to its final path. + + +.. code-block:: python + + import network_file_system as nfs + + nfs.copy(src="machine/with/src", dst="other/machine/in/network/dst") + + +move +==== + +Some implementations of network-file-systems might raise an +``OSError`` with ``errno.EXDEV`` when an ``os.rename()`` is going across the +boundary of a physical drive. +In this case, this package's ``copy`` is used to copy the file beore unlinking +the source-file. + + +.. code-block:: python + + import network_file_system as nfs + + nfs.move(src="machine/with/src", dst="other/machine/in/network/dst") + + +write +===== + +When ``with open(...`` is too much. Writes to temporary file in targt's +directory first before moving the temporary file to the target. + +.. code-block:: python + + import network_file_system as nfs + + nfs.write(content="My text.", path="another/machine/dst.txt", mode="wt") + -A python-library to make moves, copys and writes safer on remote drives. -Writing a file is not atomic. In the worst case the process writing your file -dies while the file is not complete. To at least spot such incomplete files, -all writing-operations (write, move, copy, ...) will be followed by an -atomic move. .. |BlackStyle| image:: https://img.shields.io/badge/code%20style-black-000000.svg - :target: https://github.com/psf/black + :target: https://github.com/psf/black + +.. |TestStatus| image:: https://github.com/cherenkov-plenoscope/network_file_system/actions/workflows/test.yml/badge.svg?branch=main + :target: https://github.com/cherenkov-plenoscope/network_file_system/actions/workflows/test.yml + +.. |PyPiStatus| image:: https://img.shields.io/pypi/v/network_file_system + :target: https://pypi.org/project/network_file_system + +.. |PackStyleBlack| image:: https://img.shields.io/badge/pack%20style-black-000000.svg + :target: https://github.com/cherenkov-plenoscope/black_pack diff --git a/network_file_system/__init__.py b/network_file_system/__init__.py index 39fb1e1..23290bf 100644 --- a/network_file_system/__init__.py +++ b/network_file_system/__init__.py @@ -8,6 +8,7 @@ all writing-operations (write, move, copy, ...) will be followed by an atomic move. """ +from .version import __version__ import uuid import os import shutil @@ -118,7 +119,7 @@ class NfsFileWriter: def __init__(self, file, mode, tmp_dir=None): """ tmp_dir : str (default: None) - Path to the emporary-directory where the file is initially written + Path to the temporary-directory where the file is initially written to. Make sure this is a fast drive. """ self.file = file diff --git a/network_file_system/version.py b/network_file_system/version.py new file mode 100644 index 0000000..27fdca4 --- /dev/null +++ b/network_file_system/version.py @@ -0,0 +1 @@ +__version__ = "0.0.3" diff --git a/setup.py b/setup.py index 238a363..c7c0e98 100644 --- a/setup.py +++ b/setup.py @@ -1,21 +1,30 @@ import setuptools +import os + +with open("README.rst", "r", encoding="utf-8") as f: + long_description = f.read() + + +with open(os.path.join("network_file_system", "version.py")) as f: + txt = f.read() + last_line = txt.splitlines()[-1] + version_string = last_line.split()[-1] + version = version_string.strip("\"'") -with open("README.rst", "r", encoding="utf-8") as fh: - long_description = fh.read() setuptools.setup( - name="network_file_system_sebastian-achim-mueller", - version="0.0.2", + name="network_file_system", + version=version, description="Safe copy, move, and write on remote drives", long_description=long_description, long_description_content_type="text/x-rst", url="https://github.com/cherenkov-plenoscope/network_file_system", - project_urls={ - "Bug Tracker": "https://github.com/cherenkov-plenoscope/network_file_system/issues", - }, author="Sebastian Achim Mueller", author_email="sebastian-achim.mueller@mpi-hd.mpg.de", - packages=["network_file_system",], + packages=[ + "network_file_system", + ], + package_data={"network_file_system": []}, install_requires=[], classifiers=[ "Programming Language :: Python :: 3",